From 5a433b3ad497120d80f1045d37bd1a9ce897388f Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Mon, 21 Apr 2008 10:41:10 -0700 Subject: mac80211: remove unnecessary byteshifts in frame control testing Byteswap the constants rather than the frame_control member. Signed-off-by: Harvey Harrison Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 0b5e03eae6d2..a9102bc78b61 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -552,16 +552,17 @@ enum ieee80211_back_parties { */ static inline u8 *ieee80211_get_SA(struct ieee80211_hdr *hdr) { - u8 *raw = (u8 *) hdr; - u8 tofrom = (*(raw+1)) & 3; /* get the TODS and FROMDS bits */ - - switch (tofrom) { - case 2: - return hdr->addr3; - case 3: - return hdr->addr4; + __le16 fc = hdr->frame_control; + fc &= cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS); + + switch (fc) { + case __constant_cpu_to_le16(IEEE80211_FCTL_FROMDS): + return hdr->addr3; + case __constant_cpu_to_le16(IEEE80211_FCTL_TODS|IEEE80211_FCTL_FROMDS): + return hdr->addr4; + default: + return hdr->addr2; } - return hdr->addr2; } /** @@ -577,12 +578,13 @@ static inline u8 *ieee80211_get_SA(struct ieee80211_hdr *hdr) */ static inline u8 *ieee80211_get_DA(struct ieee80211_hdr *hdr) { - u8 *raw = (u8 *) hdr; - u8 to_ds = (*(raw+1)) & 1; /* get the TODS bit */ + __le16 fc = hdr->frame_control; + fc &= cpu_to_le16(IEEE80211_FCTL_TODS); - if (to_ds) + if (fc) return hdr->addr3; - return hdr->addr1; + else + return hdr->addr1; } /** @@ -595,8 +597,8 @@ static inline u8 *ieee80211_get_DA(struct ieee80211_hdr *hdr) */ static inline int ieee80211_get_morefrag(struct ieee80211_hdr *hdr) { - return (le16_to_cpu(hdr->frame_control) & - IEEE80211_FCTL_MOREFRAGS) != 0; + __le16 fc = hdr->frame_control; + return !!(fc & cpu_to_le16(IEEE80211_FCTL_MOREFRAGS)); } #endif /* IEEE80211_H */ -- cgit v1.2.3 From 3e582db0783872a645f5236ef142be6579b300ad Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 6 May 2008 12:11:29 -0400 Subject: Input: gameport - remove rescan/reconnect facilities They have never been used and are unlikely to be used in the future so remove them altogether. Reported-by: Adrian Bunk Signed-off-by: Dmitry Torokhov --- drivers/input/gameport/gameport.c | 22 ---------------------- include/linux/gameport.h | 1 - 2 files changed, 23 deletions(-) (limited to 'include/linux') diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c index c5600ac5feb3..078e4eed0894 100644 --- a/drivers/input/gameport/gameport.c +++ b/drivers/input/gameport/gameport.c @@ -36,7 +36,6 @@ EXPORT_SYMBOL(__gameport_register_driver); EXPORT_SYMBOL(gameport_unregister_driver); EXPORT_SYMBOL(gameport_open); EXPORT_SYMBOL(gameport_close); -EXPORT_SYMBOL(gameport_rescan); EXPORT_SYMBOL(gameport_set_phys); EXPORT_SYMBOL(gameport_start_polling); EXPORT_SYMBOL(gameport_stop_polling); @@ -230,8 +229,6 @@ static void gameport_find_driver(struct gameport *gameport) */ enum gameport_event_type { - GAMEPORT_RESCAN, - GAMEPORT_RECONNECT, GAMEPORT_REGISTER_PORT, GAMEPORT_REGISTER_DRIVER, }; @@ -365,15 +362,6 @@ static void gameport_handle_event(void) gameport_add_port(event->object); break; - case GAMEPORT_RECONNECT: - gameport_reconnect_port(event->object); - break; - - case GAMEPORT_RESCAN: - gameport_disconnect_port(event->object); - gameport_find_driver(event->object); - break; - case GAMEPORT_REGISTER_DRIVER: gameport_add_driver(event->object); break; @@ -651,16 +639,6 @@ static void gameport_disconnect_port(struct gameport *gameport) device_release_driver(&gameport->dev); } -void gameport_rescan(struct gameport *gameport) -{ - gameport_queue_event(gameport, NULL, GAMEPORT_RESCAN); -} - -void gameport_reconnect(struct gameport *gameport) -{ - gameport_queue_event(gameport, NULL, GAMEPORT_RECONNECT); -} - /* * Submits register request to kgameportd for subsequent execution. * Note that port registration is always asynchronous. diff --git a/include/linux/gameport.h b/include/linux/gameport.h index afad95272841..f64e29c0ef3f 100644 --- a/include/linux/gameport.h +++ b/include/linux/gameport.h @@ -68,7 +68,6 @@ struct gameport_driver { int gameport_open(struct gameport *gameport, struct gameport_driver *drv, int mode); void gameport_close(struct gameport *gameport); -void gameport_rescan(struct gameport *gameport); #if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) -- cgit v1.2.3 From ccd3474569a101914444dc37cc108eec22578999 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 6 May 2008 12:11:39 -0400 Subject: Input: libps2 - remove delayed command execution Delayed command execution is not used by anyone so let's remove it. Reported-by: Adrian Bunk Signed-off-by: Dmitry Torokhov --- drivers/input/serio/libps2.c | 52 -------------------------------------------- include/linux/libps2.h | 1 - 2 files changed, 53 deletions(-) (limited to 'include/linux') diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index b819239d74dc..2b304c22c200 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -26,15 +26,6 @@ MODULE_AUTHOR("Dmitry Torokhov "); MODULE_DESCRIPTION("PS/2 driver library"); MODULE_LICENSE("GPL"); -/* Work structure to schedule execution of a command */ -struct ps2work { - struct work_struct work; - struct ps2dev *ps2dev; - int command; - unsigned char param[0]; -}; - - /* * ps2_sendbyte() sends a byte to the device and waits for acknowledge. * It doesn't handle retransmission, though it could - because if there @@ -245,49 +236,6 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) } EXPORT_SYMBOL(ps2_command); -/* - * ps2_execute_scheduled_command() sends a command, previously scheduled by - * ps2_schedule_command(), to a PS/2 device (keyboard, mouse, etc.) - */ - -static void ps2_execute_scheduled_command(struct work_struct *work) -{ - struct ps2work *ps2work = container_of(work, struct ps2work, work); - - ps2_command(ps2work->ps2dev, ps2work->param, ps2work->command); - kfree(ps2work); -} - -/* - * ps2_schedule_command() allows to schedule delayed execution of a PS/2 - * command and can be used to issue a command from an interrupt or softirq - * context. - */ - -int ps2_schedule_command(struct ps2dev *ps2dev, unsigned char *param, int command) -{ - struct ps2work *ps2work; - int send = (command >> 12) & 0xf; - int receive = (command >> 8) & 0xf; - - if (!(ps2work = kmalloc(sizeof(struct ps2work) + max(send, receive), GFP_ATOMIC))) - return -1; - - memset(ps2work, 0, sizeof(struct ps2work)); - ps2work->ps2dev = ps2dev; - ps2work->command = command; - memcpy(ps2work->param, param, send); - INIT_WORK(&ps2work->work, ps2_execute_scheduled_command); - - if (!schedule_work(&ps2work->work)) { - kfree(ps2work); - return -1; - } - - return 0; -} -EXPORT_SYMBOL(ps2_schedule_command); - /* * ps2_init() initializes ps2dev structure */ diff --git a/include/linux/libps2.h b/include/linux/libps2.h index f6f301e2b0f5..afc413369101 100644 --- a/include/linux/libps2.h +++ b/include/linux/libps2.h @@ -43,7 +43,6 @@ void ps2_init(struct ps2dev *ps2dev, struct serio *serio); int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout); void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout); int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command); -int ps2_schedule_command(struct ps2dev *ps2dev, unsigned char *param, int command); int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data); int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data); void ps2_cmd_aborted(struct ps2dev *ps2dev); -- cgit v1.2.3 From 253b00f1e6619055d524188f254ccb951bffce5d Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 7 May 2008 11:15:23 -0400 Subject: Input: remove unused defines Remove unused to_dev, to_handler, to_handle from include/linux/input.h Move to_handle_h from include/linux/input.h to drivers/char/keyboard.c Signed-off-by: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Dmitry Torokhov --- drivers/char/keyboard.c | 2 ++ include/linux/input.h | 5 ----- 2 files changed, 2 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index 7f7e798c1384..d0369e05490a 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -46,6 +46,8 @@ extern void ctrl_alt_del(void); +#define to_handle_h(n) container_of(n, struct input_handle, h_node) + /* * Exported functions/variables */ diff --git a/include/linux/input.h b/include/linux/input.h index 28a094fcfe20..eff711d8a459 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -1213,11 +1213,6 @@ struct input_handle { struct list_head h_node; }; -#define to_dev(n) container_of(n, struct input_dev, node) -#define to_handler(n) container_of(n, struct input_handler, node) -#define to_handle(n) container_of(n, struct input_handle, d_node) -#define to_handle_h(n) container_of(n, struct input_handle, h_node) - struct input_dev *input_allocate_device(void); void input_free_device(struct input_dev *dev); -- cgit v1.2.3 From a33466e31213cd7c46696160d3db78680b58f6a3 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 7 May 2008 16:30:15 -0400 Subject: Input: gpio-keys debouncing support Signed-off-by: Dmitry Baryshkov Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/gpio_keys.c | 89 +++++++++++++++++++++++++++++--------- include/linux/gpio_keys.h | 1 + 2 files changed, 70 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index bbd00c3fe98c..be58730e636a 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -26,23 +26,54 @@ #include +struct gpio_button_data { + struct gpio_keys_button *button; + struct input_dev *input; + struct timer_list timer; +}; + +struct gpio_keys_drvdata { + struct input_dev *input; + struct gpio_button_data data[0]; +}; + +static void gpio_keys_report_event(struct gpio_keys_button *button, + struct input_dev *input) +{ + unsigned int type = button->type ?: EV_KEY; + int state = (gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low; + + input_event(input, type, button->code, !!state); + input_sync(input); +} + +static void gpio_check_button(unsigned long _data) +{ + struct gpio_button_data *data = (struct gpio_button_data *)_data; + + gpio_keys_report_event(data->button, data->input); +} + static irqreturn_t gpio_keys_isr(int irq, void *dev_id) { - int i; struct platform_device *pdev = dev_id; struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; - struct input_dev *input = platform_get_drvdata(pdev); + struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev); + int i; for (i = 0; i < pdata->nbuttons; i++) { struct gpio_keys_button *button = &pdata->buttons[i]; - int gpio = button->gpio; - if (irq == gpio_to_irq(gpio)) { - unsigned int type = button->type ?: EV_KEY; - int state = (gpio_get_value(gpio) ? 1 : 0) ^ button->active_low; + if (irq == gpio_to_irq(button->gpio)) { + struct gpio_button_data *bdata = &ddata->data[i]; + + if (button->debounce_interval) + mod_timer(&bdata->timer, + jiffies + + msecs_to_jiffies(button->debounce_interval)); + else + gpio_keys_report_event(button, bdata->input); - input_event(input, type, button->code, !!state); - input_sync(input); return IRQ_HANDLED; } } @@ -53,17 +84,21 @@ static irqreturn_t gpio_keys_isr(int irq, void *dev_id) static int __devinit gpio_keys_probe(struct platform_device *pdev) { struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; + struct gpio_keys_drvdata *ddata; struct input_dev *input; int i, error; int wakeup = 0; + ddata = kzalloc(sizeof(struct gpio_keys_drvdata) + + pdata->nbuttons * sizeof(struct gpio_button_data), + GFP_KERNEL); input = input_allocate_device(); - if (!input) - return -ENOMEM; - - platform_set_drvdata(pdev, input); + if (!ddata || !input) { + error = -ENOMEM; + goto fail1; + } - input->evbit[0] = BIT_MASK(EV_KEY); + platform_set_drvdata(pdev, ddata); input->name = pdev->name; input->phys = "gpio-keys/input0"; @@ -74,16 +109,23 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) input->id.product = 0x0001; input->id.version = 0x0100; + ddata->input = input; + for (i = 0; i < pdata->nbuttons; i++) { struct gpio_keys_button *button = &pdata->buttons[i]; + struct gpio_button_data *bdata = &ddata->data[i]; int irq; unsigned int type = button->type ?: EV_KEY; + bdata->input = input; + setup_timer(&bdata->timer, + gpio_check_button, (unsigned long)bdata); + error = gpio_request(button->gpio, button->desc ?: "gpio_keys"); if (error < 0) { pr_err("gpio-keys: failed to request GPIO %d," " error %d\n", button->gpio, error); - goto fail; + goto fail2; } error = gpio_direction_input(button->gpio); @@ -92,7 +134,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) " direction for GPIO %d, error %d\n", button->gpio, error); gpio_free(button->gpio); - goto fail; + goto fail2; } irq = gpio_to_irq(button->gpio); @@ -102,7 +144,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) " for GPIO %d, error %d\n", button->gpio, error); gpio_free(button->gpio); - goto fail; + goto fail2; } error = request_irq(irq, gpio_keys_isr, @@ -114,7 +156,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) pr_err("gpio-keys: Unable to claim irq %d; error %d\n", irq, error); gpio_free(button->gpio); - goto fail; + goto fail2; } if (button->wakeup) @@ -127,21 +169,25 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) if (error) { pr_err("gpio-keys: Unable to register input device, " "error: %d\n", error); - goto fail; + goto fail2; } device_init_wakeup(&pdev->dev, wakeup); return 0; - fail: + fail2: while (--i >= 0) { free_irq(gpio_to_irq(pdata->buttons[i].gpio), pdev); + if (pdata->buttons[i].debounce_interval) + del_timer_sync(&ddata->data[i].timer); gpio_free(pdata->buttons[i].gpio); } platform_set_drvdata(pdev, NULL); + fail1: input_free_device(input); + kfree(ddata); return error; } @@ -149,7 +195,8 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) static int __devexit gpio_keys_remove(struct platform_device *pdev) { struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; - struct input_dev *input = platform_get_drvdata(pdev); + struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev); + struct input_dev *input = ddata->input; int i; device_init_wakeup(&pdev->dev, 0); @@ -157,6 +204,8 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev) for (i = 0; i < pdata->nbuttons; i++) { int irq = gpio_to_irq(pdata->buttons[i].gpio); free_irq(irq, pdev); + if (pdata->buttons[i].debounce_interval) + del_timer_sync(&ddata->data[i].timer); gpio_free(pdata->buttons[i].gpio); } diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h index c6d3a9de5634..ec6ecd74781d 100644 --- a/include/linux/gpio_keys.h +++ b/include/linux/gpio_keys.h @@ -9,6 +9,7 @@ struct gpio_keys_button { char *desc; int type; /* input event type (EV_KEY, EV_SW) */ int wakeup; /* configure the button as a wake-up source */ + int debounce_interval; /* debounce ticks interval in msecs */ }; struct gpio_keys_platform_data { -- cgit v1.2.3 From 48adcf148c83faa41999fb0b3524299c4e160fd9 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 20 May 2008 01:03:16 +0300 Subject: [CPUFREQ] cpufreq: remove CVS keywords This patch removes CVS keywords that weren't updated for a long time from comments. Signed-off-by: Adrian Bunk Signed-off-by: Dave Jones --- arch/x86/kernel/cpu/cpufreq/powernow-k7.h | 1 - include/linux/cpufreq.h | 3 --- 2 files changed, 4 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/cpu/cpufreq/powernow-k7.h b/arch/x86/kernel/cpu/cpufreq/powernow-k7.h index f8a63b3664e3..35fb4eaf6e1c 100644 --- a/arch/x86/kernel/cpu/cpufreq/powernow-k7.h +++ b/arch/x86/kernel/cpu/cpufreq/powernow-k7.h @@ -1,5 +1,4 @@ /* - * $Id: powernow-k7.h,v 1.2 2003/02/10 18:26:01 davej Exp $ * (C) 2003 Dave Jones. * * Licensed under the terms of the GNU GPL License version 2. diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index e7e91dbfde0f..2270ca5ec631 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -4,9 +4,6 @@ * Copyright (C) 2001 Russell King * (C) 2002 - 2003 Dominik Brodowski * - * - * $Id: cpufreq.h,v 1.36 2003/01/20 17:31:48 db Exp $ - * * 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. -- cgit v1.2.3 From d1659fcc59b21ec442564fedb67a5ad371f82380 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 20 May 2008 12:17:39 -0400 Subject: Input: remove CVS keywords This patch removes CVS keywords that weren't updated for a long time from comments. Signed-off-by: Adrian Bunk Signed-off-by: Dmitry Torokhov --- Documentation/input/gameport-programming.txt | 2 -- Documentation/input/input.txt | 1 - Documentation/input/joystick-api.txt | 2 -- Documentation/input/joystick-parport.txt | 1 - Documentation/input/joystick.txt | 1 - drivers/input/evbug.c | 2 -- drivers/input/gameport/emu10k1-gp.c | 2 -- drivers/input/gameport/lightning.c | 2 -- drivers/input/gameport/ns558.c | 2 -- drivers/input/joystick/a3d.c | 2 -- drivers/input/joystick/amijoy.c | 2 -- drivers/input/joystick/cobra.c | 2 -- drivers/input/joystick/db9.c | 2 -- drivers/input/joystick/gf2k.c | 2 -- drivers/input/joystick/grip.c | 2 -- drivers/input/joystick/grip_mp.c | 2 -- drivers/input/joystick/guillemot.c | 2 -- drivers/input/joystick/iforce/iforce-ff.c | 2 -- drivers/input/joystick/iforce/iforce-main.c | 2 -- drivers/input/joystick/iforce/iforce-packets.c | 2 -- drivers/input/joystick/iforce/iforce-serio.c | 2 -- drivers/input/joystick/iforce/iforce-usb.c | 2 -- drivers/input/joystick/iforce/iforce.h | 2 -- drivers/input/joystick/interact.c | 2 -- drivers/input/joystick/joydump.c | 2 -- drivers/input/joystick/magellan.c | 2 -- drivers/input/joystick/spaceball.c | 2 -- drivers/input/joystick/spaceorb.c | 2 -- drivers/input/joystick/stinger.c | 2 -- drivers/input/joystick/tmdc.c | 2 -- drivers/input/joystick/turbografx.c | 2 -- drivers/input/joystick/twidjoy.c | 4 ---- drivers/input/joystick/warrior.c | 2 -- drivers/input/keyboard/amikbd.c | 2 -- drivers/input/keyboard/sunkbd.c | 2 -- drivers/input/keyboard/xtkbd.c | 2 -- drivers/input/mouse/inport.c | 2 -- drivers/input/mouse/logibm.c | 2 -- drivers/input/mouse/pc110pad.c | 2 -- drivers/input/mouse/sermouse.c | 2 -- drivers/input/serio/ct82c710.c | 2 -- drivers/input/serio/q40kbd.c | 2 -- drivers/input/serio/rpckbd.c | 2 -- drivers/input/touchscreen/gunze.c | 2 -- drivers/input/touchscreen/h3600_ts_input.c | 2 -- include/linux/joystick.h | 2 -- 46 files changed, 91 deletions(-) (limited to 'include/linux') diff --git a/Documentation/input/gameport-programming.txt b/Documentation/input/gameport-programming.txt index 14e0a8b70225..03a74fc3b496 100644 --- a/Documentation/input/gameport-programming.txt +++ b/Documentation/input/gameport-programming.txt @@ -1,5 +1,3 @@ -$Id: gameport-programming.txt,v 1.3 2001/04/24 13:51:37 vojtech Exp $ - Programming gameport drivers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/Documentation/input/input.txt b/Documentation/input/input.txt index ff8cea0225f9..686ee9932dff 100644 --- a/Documentation/input/input.txt +++ b/Documentation/input/input.txt @@ -1,7 +1,6 @@ Linux Input drivers v1.0 (c) 1999-2001 Vojtech Pavlik Sponsored by SuSE - $Id: input.txt,v 1.8 2002/05/29 03:15:01 bradleym Exp $ ---------------------------------------------------------------------------- 0. Disclaimer diff --git a/Documentation/input/joystick-api.txt b/Documentation/input/joystick-api.txt index acbd32b88454..c507330740cd 100644 --- a/Documentation/input/joystick-api.txt +++ b/Documentation/input/joystick-api.txt @@ -5,8 +5,6 @@ 7 Aug 1998 - $Id: joystick-api.txt,v 1.2 2001/05/08 21:21:23 vojtech Exp $ - 1. Initialization ~~~~~~~~~~~~~~~~~ diff --git a/Documentation/input/joystick-parport.txt b/Documentation/input/joystick-parport.txt index ede5f33daad3..1c856f32ff2c 100644 --- a/Documentation/input/joystick-parport.txt +++ b/Documentation/input/joystick-parport.txt @@ -2,7 +2,6 @@ (c) 1998-2000 Vojtech Pavlik (c) 1998 Andree Borrmann Sponsored by SuSE - $Id: joystick-parport.txt,v 1.6 2001/09/25 09:31:32 vojtech Exp $ ---------------------------------------------------------------------------- 0. Disclaimer diff --git a/Documentation/input/joystick.txt b/Documentation/input/joystick.txt index 389de9bd9878..154d767b2acb 100644 --- a/Documentation/input/joystick.txt +++ b/Documentation/input/joystick.txt @@ -1,7 +1,6 @@ Linux Joystick driver v2.0.0 (c) 1996-2000 Vojtech Pavlik Sponsored by SuSE - $Id: joystick.txt,v 1.12 2002/03/03 12:13:07 jdeneux Exp $ ---------------------------------------------------------------------------- 0. Disclaimer diff --git a/drivers/input/evbug.c b/drivers/input/evbug.c index c21f2f127234..67754c776d88 100644 --- a/drivers/input/evbug.c +++ b/drivers/input/evbug.c @@ -1,6 +1,4 @@ /* - * $Id: evbug.c,v 1.10 2001/09/25 10:12:07 vojtech Exp $ - * * Copyright (c) 1999-2001 Vojtech Pavlik */ diff --git a/drivers/input/gameport/emu10k1-gp.c b/drivers/input/gameport/emu10k1-gp.c index 9793ac36d17f..b04930f7ea7d 100644 --- a/drivers/input/gameport/emu10k1-gp.c +++ b/drivers/input/gameport/emu10k1-gp.c @@ -1,6 +1,4 @@ /* - * $Id: emu10k1-gp.c,v 1.8 2002/01/22 20:40:46 vojtech Exp $ - * * Copyright (c) 2001 Vojtech Pavlik */ diff --git a/drivers/input/gameport/lightning.c b/drivers/input/gameport/lightning.c index 6b4d4561d465..06ad36ed3483 100644 --- a/drivers/input/gameport/lightning.c +++ b/drivers/input/gameport/lightning.c @@ -1,6 +1,4 @@ /* - * $Id: lightning.c,v 1.20 2002/01/22 20:41:31 vojtech Exp $ - * * Copyright (c) 1998-2001 Vojtech Pavlik */ diff --git a/drivers/input/gameport/ns558.c b/drivers/input/gameport/ns558.c index 7b7a546323cf..2b282cde4b89 100644 --- a/drivers/input/gameport/ns558.c +++ b/drivers/input/gameport/ns558.c @@ -1,6 +1,4 @@ /* - * $Id: ns558.c,v 1.43 2002/01/24 19:23:21 vojtech Exp $ - * * Copyright (c) 1999-2001 Vojtech Pavlik * Copyright (c) 1999 Brian Gerst */ diff --git a/drivers/input/joystick/a3d.c b/drivers/input/joystick/a3d.c index 52ba16f487c7..92498d470b1f 100644 --- a/drivers/input/joystick/a3d.c +++ b/drivers/input/joystick/a3d.c @@ -1,6 +1,4 @@ /* - * $Id: a3d.c,v 1.21 2002/01/22 20:11:50 vojtech Exp $ - * * Copyright (c) 1998-2001 Vojtech Pavlik */ diff --git a/drivers/input/joystick/amijoy.c b/drivers/input/joystick/amijoy.c index deb9f825f92c..05022f07ec77 100644 --- a/drivers/input/joystick/amijoy.c +++ b/drivers/input/joystick/amijoy.c @@ -1,6 +1,4 @@ /* - * $Id: amijoy.c,v 1.13 2002/01/22 20:26:32 vojtech Exp $ - * * Copyright (c) 1998-2001 Vojtech Pavlik */ diff --git a/drivers/input/joystick/cobra.c b/drivers/input/joystick/cobra.c index 55646a6d89f5..639b975a8ed7 100644 --- a/drivers/input/joystick/cobra.c +++ b/drivers/input/joystick/cobra.c @@ -1,6 +1,4 @@ /* - * $Id: cobra.c,v 1.19 2002/01/22 20:26:52 vojtech Exp $ - * * Copyright (c) 1999-2001 Vojtech Pavlik */ diff --git a/drivers/input/joystick/db9.c b/drivers/input/joystick/db9.c index 960e501c60c8..523959484753 100644 --- a/drivers/input/joystick/db9.c +++ b/drivers/input/joystick/db9.c @@ -1,6 +1,4 @@ /* - * $Id: db9.c,v 1.13 2002/04/07 20:13:37 vojtech Exp $ - * * Copyright (c) 1999-2001 Vojtech Pavlik * * Based on the work of: diff --git a/drivers/input/joystick/gf2k.c b/drivers/input/joystick/gf2k.c index 1f6302c0eb3f..cb6eef1f2d99 100644 --- a/drivers/input/joystick/gf2k.c +++ b/drivers/input/joystick/gf2k.c @@ -1,6 +1,4 @@ /* - * $Id: gf2k.c,v 1.19 2002/01/22 20:27:43 vojtech Exp $ - * * Copyright (c) 1998-2001 Vojtech Pavlik */ diff --git a/drivers/input/joystick/grip.c b/drivers/input/joystick/grip.c index fd3853ab1aad..684e07cfccc8 100644 --- a/drivers/input/joystick/grip.c +++ b/drivers/input/joystick/grip.c @@ -1,6 +1,4 @@ /* - * $Id: grip.c,v 1.21 2002/01/22 20:27:57 vojtech Exp $ - * * Copyright (c) 1998-2001 Vojtech Pavlik */ diff --git a/drivers/input/joystick/grip_mp.c b/drivers/input/joystick/grip_mp.c index c57e21d68c00..8279481b16e7 100644 --- a/drivers/input/joystick/grip_mp.c +++ b/drivers/input/joystick/grip_mp.c @@ -1,6 +1,4 @@ /* - * $Id: grip_mp.c,v 1.9 2002/07/20 19:28:45 bonnland Exp $ - * * Driver for the Gravis Grip Multiport, a gamepad "hub" that * connects up to four 9-pin digital gamepads/joysticks. * Driver tested on SMP and UP kernel versions 2.4.18-4 and 2.4.18-5. diff --git a/drivers/input/joystick/guillemot.c b/drivers/input/joystick/guillemot.c index aa6bfb3fb8cd..25ec3fad9f27 100644 --- a/drivers/input/joystick/guillemot.c +++ b/drivers/input/joystick/guillemot.c @@ -1,6 +1,4 @@ /* - * $Id: guillemot.c,v 1.10 2002/01/22 20:28:12 vojtech Exp $ - * * Copyright (c) 2001 Vojtech Pavlik */ diff --git a/drivers/input/joystick/iforce/iforce-ff.c b/drivers/input/joystick/iforce/iforce-ff.c index f2a4381d0ab8..7839b7b6fa96 100644 --- a/drivers/input/joystick/iforce/iforce-ff.c +++ b/drivers/input/joystick/iforce/iforce-ff.c @@ -1,6 +1,4 @@ /* - * $Id: iforce-ff.c,v 1.9 2002/02/02 19:28:35 jdeneux Exp $ - * * Copyright (c) 2000-2002 Vojtech Pavlik * Copyright (c) 2001-2002, 2007 Johann Deneux * diff --git a/drivers/input/joystick/iforce/iforce-main.c b/drivers/input/joystick/iforce/iforce-main.c index a2517fa72eb8..61ee6e38739d 100644 --- a/drivers/input/joystick/iforce/iforce-main.c +++ b/drivers/input/joystick/iforce/iforce-main.c @@ -1,6 +1,4 @@ /* - * $Id: iforce-main.c,v 1.19 2002/07/07 10:22:50 jdeneux Exp $ - * * Copyright (c) 2000-2002 Vojtech Pavlik * Copyright (c) 2001-2002, 2007 Johann Deneux * diff --git a/drivers/input/joystick/iforce/iforce-packets.c b/drivers/input/joystick/iforce/iforce-packets.c index 45c4939ced75..015b50aa76fc 100644 --- a/drivers/input/joystick/iforce/iforce-packets.c +++ b/drivers/input/joystick/iforce/iforce-packets.c @@ -1,6 +1,4 @@ /* - * $Id: iforce-packets.c,v 1.16 2002/07/07 10:22:50 jdeneux Exp $ - * * Copyright (c) 2000-2002 Vojtech Pavlik * Copyright (c) 2001-2002, 2007 Johann Deneux * diff --git a/drivers/input/joystick/iforce/iforce-serio.c b/drivers/input/joystick/iforce/iforce-serio.c index 7b4bc19cef27..46d5041d2d9d 100644 --- a/drivers/input/joystick/iforce/iforce-serio.c +++ b/drivers/input/joystick/iforce/iforce-serio.c @@ -1,6 +1,4 @@ /* - * $Id: iforce-serio.c,v 1.4 2002/01/28 22:45:00 jdeneux Exp $ - * * Copyright (c) 2000-2001 Vojtech Pavlik * Copyright (c) 2001, 2007 Johann Deneux * diff --git a/drivers/input/joystick/iforce/iforce-usb.c b/drivers/input/joystick/iforce/iforce-usb.c index 8f6a987ebb9f..851cc4087c2f 100644 --- a/drivers/input/joystick/iforce/iforce-usb.c +++ b/drivers/input/joystick/iforce/iforce-usb.c @@ -1,6 +1,4 @@ /* - * $Id: iforce-usb.c,v 1.16 2002/06/09 11:08:04 jdeneux Exp $ - * * Copyright (c) 2000-2002 Vojtech Pavlik * Copyright (c) 2001-2002, 2007 Johann Deneux * diff --git a/drivers/input/joystick/iforce/iforce.h b/drivers/input/joystick/iforce/iforce.h index a964a7cfd210..f2d91f4028ca 100644 --- a/drivers/input/joystick/iforce/iforce.h +++ b/drivers/input/joystick/iforce/iforce.h @@ -1,6 +1,4 @@ /* - * $Id: iforce.h,v 1.13 2002/07/07 10:22:50 jdeneux Exp $ - * * Copyright (c) 2000-2002 Vojtech Pavlik * Copyright (c) 2001-2002, 2007 Johann Deneux * diff --git a/drivers/input/joystick/interact.c b/drivers/input/joystick/interact.c index bc8ea95dfd0e..8c3290b68205 100644 --- a/drivers/input/joystick/interact.c +++ b/drivers/input/joystick/interact.c @@ -1,6 +1,4 @@ /* - * $Id: interact.c,v 1.16 2002/01/22 20:28:25 vojtech Exp $ - * * Copyright (c) 2001 Vojtech Pavlik * * Based on the work of: diff --git a/drivers/input/joystick/joydump.c b/drivers/input/joystick/joydump.c index 88ec5a918f2e..2a1b82c8b31c 100644 --- a/drivers/input/joystick/joydump.c +++ b/drivers/input/joystick/joydump.c @@ -1,6 +1,4 @@ /* - * $Id: joydump.c,v 1.1 2002/01/23 06:56:16 jsimmons Exp $ - * * Copyright (c) 1996-2001 Vojtech Pavlik */ diff --git a/drivers/input/joystick/magellan.c b/drivers/input/joystick/magellan.c index 54e676948ebb..40e40780747d 100644 --- a/drivers/input/joystick/magellan.c +++ b/drivers/input/joystick/magellan.c @@ -1,6 +1,4 @@ /* - * $Id: magellan.c,v 1.16 2002/01/22 20:28:39 vojtech Exp $ - * * Copyright (c) 1999-2001 Vojtech Pavlik */ diff --git a/drivers/input/joystick/spaceball.c b/drivers/input/joystick/spaceball.c index d4087fd49656..0cd9b29356a8 100644 --- a/drivers/input/joystick/spaceball.c +++ b/drivers/input/joystick/spaceball.c @@ -1,6 +1,4 @@ /* - * $Id: spaceball.c,v 1.17 2002/01/22 20:29:03 vojtech Exp $ - * * Copyright (c) 1999-2001 Vojtech Pavlik * * Based on the work of: diff --git a/drivers/input/joystick/spaceorb.c b/drivers/input/joystick/spaceorb.c index f7ce4004f4ba..a694bf8e557b 100644 --- a/drivers/input/joystick/spaceorb.c +++ b/drivers/input/joystick/spaceorb.c @@ -1,6 +1,4 @@ /* - * $Id: spaceorb.c,v 1.15 2002/01/22 20:29:19 vojtech Exp $ - * * Copyright (c) 1999-2001 Vojtech Pavlik * * Based on the work of: diff --git a/drivers/input/joystick/stinger.c b/drivers/input/joystick/stinger.c index baa10b2f7ba1..e0db9f5e4b41 100644 --- a/drivers/input/joystick/stinger.c +++ b/drivers/input/joystick/stinger.c @@ -1,6 +1,4 @@ /* - * $Id: stinger.c,v 1.10 2002/01/22 20:29:31 vojtech Exp $ - * * Copyright (c) 2000-2001 Vojtech Pavlik * Copyright (c) 2000 Mark Fletcher */ diff --git a/drivers/input/joystick/tmdc.c b/drivers/input/joystick/tmdc.c index 0feeb8acb532..60c37bcb938d 100644 --- a/drivers/input/joystick/tmdc.c +++ b/drivers/input/joystick/tmdc.c @@ -1,6 +1,4 @@ /* - * $Id: tmdc.c,v 1.31 2002/01/22 20:29:52 vojtech Exp $ - * * Copyright (c) 1998-2001 Vojtech Pavlik * * Based on the work of: diff --git a/drivers/input/joystick/turbografx.c b/drivers/input/joystick/turbografx.c index 989483f53160..b6f859869540 100644 --- a/drivers/input/joystick/turbografx.c +++ b/drivers/input/joystick/turbografx.c @@ -1,6 +1,4 @@ /* - * $Id: turbografx.c,v 1.14 2002/01/22 20:30:39 vojtech Exp $ - * * Copyright (c) 1998-2001 Vojtech Pavlik * * Based on the work of: diff --git a/drivers/input/joystick/twidjoy.c b/drivers/input/joystick/twidjoy.c index 1085c841fec4..3f4ec73c9553 100644 --- a/drivers/input/joystick/twidjoy.c +++ b/drivers/input/joystick/twidjoy.c @@ -1,8 +1,4 @@ /* - * $Id: twidjoy.c,v 1.5 2002/01/22 20:31:53 vojtech Exp $ - * - * derived from CVS-ID "stinger.c,v 1.5 2001/05/29 12:57:18 vojtech Exp" - * * Copyright (c) 2001 Arndt Schoenewald * Copyright (c) 2000-2001 Vojtech Pavlik * Copyright (c) 2000 Mark Fletcher diff --git a/drivers/input/joystick/warrior.c b/drivers/input/joystick/warrior.c index e928b6e3724a..f72c83e15e60 100644 --- a/drivers/input/joystick/warrior.c +++ b/drivers/input/joystick/warrior.c @@ -1,6 +1,4 @@ /* - * $Id: warrior.c,v 1.14 2002/01/22 20:32:10 vojtech Exp $ - * * Copyright (c) 1999-2001 Vojtech Pavlik */ diff --git a/drivers/input/keyboard/amikbd.c b/drivers/input/keyboard/amikbd.c index 81bf7562aca0..35149ec455a9 100644 --- a/drivers/input/keyboard/amikbd.c +++ b/drivers/input/keyboard/amikbd.c @@ -1,6 +1,4 @@ /* - * $Id: amikbd.c,v 1.13 2002/02/01 16:02:24 vojtech Exp $ - * * Copyright (c) 2000-2001 Vojtech Pavlik * * Based on the work of: diff --git a/drivers/input/keyboard/sunkbd.c b/drivers/input/keyboard/sunkbd.c index be0f5d19d023..9fce6d1e29b2 100644 --- a/drivers/input/keyboard/sunkbd.c +++ b/drivers/input/keyboard/sunkbd.c @@ -1,6 +1,4 @@ /* - * $Id: sunkbd.c,v 1.14 2001/09/25 10:12:07 vojtech Exp $ - * * Copyright (c) 1999-2001 Vojtech Pavlik */ diff --git a/drivers/input/keyboard/xtkbd.c b/drivers/input/keyboard/xtkbd.c index 152a2c070508..37b01d777a4a 100644 --- a/drivers/input/keyboard/xtkbd.c +++ b/drivers/input/keyboard/xtkbd.c @@ -1,6 +1,4 @@ /* - * $Id: xtkbd.c,v 1.11 2001/09/25 10:12:07 vojtech Exp $ - * * Copyright (c) 1999-2001 Vojtech Pavlik */ diff --git a/drivers/input/mouse/inport.c b/drivers/input/mouse/inport.c index 06c35fc553c0..3827a22362de 100644 --- a/drivers/input/mouse/inport.c +++ b/drivers/input/mouse/inport.c @@ -1,6 +1,4 @@ /* - * $Id: inport.c,v 1.11 2001/09/25 10:12:07 vojtech Exp $ - * * Copyright (c) 1999-2001 Vojtech Pavlik * * Based on the work of: diff --git a/drivers/input/mouse/logibm.c b/drivers/input/mouse/logibm.c index 9ea895593b27..e2413113df22 100644 --- a/drivers/input/mouse/logibm.c +++ b/drivers/input/mouse/logibm.c @@ -1,6 +1,4 @@ /* - * $Id: logibm.c,v 1.11 2001/09/25 10:12:07 vojtech Exp $ - * * Copyright (c) 1999-2001 Vojtech Pavlik * * Based on the work of: diff --git a/drivers/input/mouse/pc110pad.c b/drivers/input/mouse/pc110pad.c index 61cff8374e6c..fd09c8df81f2 100644 --- a/drivers/input/mouse/pc110pad.c +++ b/drivers/input/mouse/pc110pad.c @@ -1,6 +1,4 @@ /* - * $Id: pc110pad.c,v 1.12 2001/09/25 10:12:07 vojtech Exp $ - * * Copyright (c) 2000-2001 Vojtech Pavlik * * Based on the work of: diff --git a/drivers/input/mouse/sermouse.c b/drivers/input/mouse/sermouse.c index ed917bfd086a..17ff137b9bd5 100644 --- a/drivers/input/mouse/sermouse.c +++ b/drivers/input/mouse/sermouse.c @@ -1,6 +1,4 @@ /* - * $Id: sermouse.c,v 1.17 2002/03/13 10:03:43 vojtech Exp $ - * * Copyright (c) 1999-2001 Vojtech Pavlik */ diff --git a/drivers/input/serio/ct82c710.c b/drivers/input/serio/ct82c710.c index 0d35018c23a9..d1380fc72cc6 100644 --- a/drivers/input/serio/ct82c710.c +++ b/drivers/input/serio/ct82c710.c @@ -1,6 +1,4 @@ /* - * $Id: ct82c710.c,v 1.11 2001/09/25 10:12:07 vojtech Exp $ - * * Copyright (c) 1999-2001 Vojtech Pavlik */ diff --git a/drivers/input/serio/q40kbd.c b/drivers/input/serio/q40kbd.c index cb89aff2e160..eca125c8d535 100644 --- a/drivers/input/serio/q40kbd.c +++ b/drivers/input/serio/q40kbd.c @@ -1,6 +1,4 @@ /* - * $Id: q40kbd.c,v 1.12 2002/02/02 22:26:44 vojtech Exp $ - * * Copyright (c) 2000-2001 Vojtech Pavlik * * Based on the work of: diff --git a/drivers/input/serio/rpckbd.c b/drivers/input/serio/rpckbd.c index 34c59d9c6205..1567b7782478 100644 --- a/drivers/input/serio/rpckbd.c +++ b/drivers/input/serio/rpckbd.c @@ -1,6 +1,4 @@ /* - * $Id: rpckbd.c,v 1.7 2001/09/25 10:12:07 vojtech Exp $ - * * Copyright (c) 2000-2001 Vojtech Pavlik * Copyright (c) 2002 Russell King */ diff --git a/drivers/input/touchscreen/gunze.c b/drivers/input/touchscreen/gunze.c index a48a15868c4a..a54f90e02ab6 100644 --- a/drivers/input/touchscreen/gunze.c +++ b/drivers/input/touchscreen/gunze.c @@ -1,6 +1,4 @@ /* - * $Id: gunze.c,v 1.12 2001/09/25 10:12:07 vojtech Exp $ - * * Copyright (c) 2000-2001 Vojtech Pavlik */ diff --git a/drivers/input/touchscreen/h3600_ts_input.c b/drivers/input/touchscreen/h3600_ts_input.c index 28ae15ed12c5..4f86081dc7fc 100644 --- a/drivers/input/touchscreen/h3600_ts_input.c +++ b/drivers/input/touchscreen/h3600_ts_input.c @@ -1,6 +1,4 @@ /* - * $Id: h3600_ts_input.c,v 1.4 2002/01/23 06:39:37 jsimmons Exp $ - * * Copyright (c) 2001 "Crazy" James Simmons jsimmons@transvirtual.com * * Sponsored by Transvirtual Technology. diff --git a/include/linux/joystick.h b/include/linux/joystick.h index e2d3a18af456..b5e051295a67 100644 --- a/include/linux/joystick.h +++ b/include/linux/joystick.h @@ -2,8 +2,6 @@ #define _LINUX_JOYSTICK_H /* - * $Id: joystick.h,v 1.3 2000/11/30 11:07:05 vojtech Exp $ - * * Copyright (C) 1996-2000 Vojtech Pavlik * * Sponsored by SuSE -- cgit v1.2.3 From 59f0c4523fdea865fab7d69d878269992a9d08dd Mon Sep 17 00:00:00 2001 From: Allan Stephens Date: Wed, 21 May 2008 14:52:30 -0700 Subject: tipc: Fix skb_under_panic when configuring TIPC without privileges This patch prevents a TIPC configuration command requiring network administrator privileges from triggering an skbuff underrun if it is issued by a process lacking those privileges. The revised error handling code avoids the use of a potentially uninitialized global variable by transforming the unauthorized command into a new command, then following the standard command processing path to generate the required error message. Signed-off-by: Allan Stephens Signed-off-by: David S. Miller --- include/linux/tipc_config.h | 10 +++++++++- net/tipc/config.c | 6 +++++- net/tipc/netlink.c | 16 +++++++++------- 3 files changed, 23 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tipc_config.h b/include/linux/tipc_config.h index b0c916d1f375..2bc6fa4adeb5 100644 --- a/include/linux/tipc_config.h +++ b/include/linux/tipc_config.h @@ -2,7 +2,7 @@ * include/linux/tipc_config.h: Include file for TIPC configuration interface * * Copyright (c) 2003-2006, Ericsson AB - * Copyright (c) 2005, Wind River Systems + * Copyright (c) 2005-2007, Wind River Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -135,6 +135,14 @@ #define TIPC_CMD_SET_MAX_SLAVES 0x800A /* tx unsigned, rx none */ #define TIPC_CMD_SET_NETID 0x800B /* tx unsigned, rx none */ +/* + * Reserved commands: + * May not be issued by any process. + * Used internally by TIPC. + */ + +#define TIPC_CMD_NOT_NET_ADMIN 0xC001 /* tx none, rx none */ + /* * TLV types defined for TIPC */ diff --git a/net/tipc/config.c b/net/tipc/config.c index 91d56f8fee9f..16e7cb74969b 100644 --- a/net/tipc/config.c +++ b/net/tipc/config.c @@ -2,7 +2,7 @@ * net/tipc/config.c: TIPC configuration management code * * Copyright (c) 2002-2006, Ericsson AB - * Copyright (c) 2004-2006, Wind River Systems + * Copyright (c) 2004-2007, Wind River Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -602,6 +602,10 @@ struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area case TIPC_CMD_GET_NETID: rep_tlv_buf = tipc_cfg_reply_unsigned(tipc_net_id); break; + case TIPC_CMD_NOT_NET_ADMIN: + rep_tlv_buf = + tipc_cfg_reply_error_string(TIPC_CFG_NOT_NET_ADMIN); + break; default: rep_tlv_buf = tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED " (unknown command)"); diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c index 6a7f7b4c2595..c387217bb230 100644 --- a/net/tipc/netlink.c +++ b/net/tipc/netlink.c @@ -2,7 +2,7 @@ * net/tipc/netlink.c: TIPC configuration handling * * Copyright (c) 2005-2006, Ericsson AB - * Copyright (c) 2005, Wind River Systems + * Copyright (c) 2005-2007, Wind River Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -45,15 +45,17 @@ static int handle_cmd(struct sk_buff *skb, struct genl_info *info) struct nlmsghdr *req_nlh = info->nlhdr; struct tipc_genlmsghdr *req_userhdr = info->userhdr; int hdr_space = NLMSG_SPACE(GENL_HDRLEN + TIPC_GENL_HDRLEN); + u16 cmd; if ((req_userhdr->cmd & 0xC000) && (!capable(CAP_NET_ADMIN))) - rep_buf = tipc_cfg_reply_error_string(TIPC_CFG_NOT_NET_ADMIN); + cmd = TIPC_CMD_NOT_NET_ADMIN; else - rep_buf = tipc_cfg_do_cmd(req_userhdr->dest, - req_userhdr->cmd, - NLMSG_DATA(req_nlh) + GENL_HDRLEN + TIPC_GENL_HDRLEN, - NLMSG_PAYLOAD(req_nlh, GENL_HDRLEN + TIPC_GENL_HDRLEN), - hdr_space); + cmd = req_userhdr->cmd; + + rep_buf = tipc_cfg_do_cmd(req_userhdr->dest, cmd, + NLMSG_DATA(req_nlh) + GENL_HDRLEN + TIPC_GENL_HDRLEN, + NLMSG_PAYLOAD(req_nlh, GENL_HDRLEN + TIPC_GENL_HDRLEN), + hdr_space); if (rep_buf) { skb_push(rep_buf, hdr_space); -- cgit v1.2.3 From edcdf8b21ac920e06b4180246123fe43b022e020 Mon Sep 17 00:00:00 2001 From: Ron Rindjunsky Date: Thu, 15 May 2008 13:53:55 +0800 Subject: mac80211: separate Tx and Rx MCS when configuring HT This patch follows the 11n spec in separation between Tx and Rx MCS capabilities. Up until now, when configuring the HT possible set of Tx MCS only Rx MCS were considered, assuming they are the same as the Tx MCS. This patch fixed this by looking at low level driver Tx capabilities. Signed-off-by: Ron Rindjunsky Signed-off-by: Tomas Winkler Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl4965-base.c | 14 +++-- include/linux/ieee80211.h | 18 ++++-- net/mac80211/main.c | 96 ++++++++++++++++++----------- 3 files changed, 83 insertions(+), 45 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/wireless/iwlwifi/iwl4965-base.c b/drivers/net/wireless/iwlwifi/iwl4965-base.c index 1a2d3768867d..c713b27ec653 100644 --- a/drivers/net/wireless/iwlwifi/iwl4965-base.c +++ b/drivers/net/wireless/iwlwifi/iwl4965-base.c @@ -3958,6 +3958,13 @@ static void iwl4965_dealloc_ucode_pci(struct iwl_priv *priv) iwl_free_fw_desc(priv->pci_dev, &priv->ucode_boot); } +static void iwl4965_nic_start(struct iwl_priv *priv) +{ + /* Remove all resets to allow NIC to operate */ + iwl_write32(priv, CSR_RESET, 0); +} + + /** * iwl4965_read_ucode - Read uCode images from disk file. * @@ -4447,8 +4454,7 @@ static int __iwl4965_up(struct iwl_priv *priv) } /* start card; "initialize" will load runtime ucode */ - /* Remove all resets to allow NIC to operate */ - iwl_write32(priv, CSR_RESET, 0); + iwl4965_nic_start(priv); IWL_DEBUG_INFO(DRV_NAME " is coming up\n"); @@ -6842,10 +6848,6 @@ static int __init iwl4965_init(void) return ret; - -#ifdef CONFIG_IWLWIFI_DEBUG - pci_unregister_driver(&iwl_driver); -#endif error_register: iwl4965_rate_control_unregister(); return ret; diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index a9102bc78b61..3c2ac0c37aa8 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -306,8 +306,18 @@ struct ieee80211_ht_addt_info { #define IEEE80211_HT_CAP_SGI_40 0x0040 #define IEEE80211_HT_CAP_DELAY_BA 0x0400 #define IEEE80211_HT_CAP_MAX_AMSDU 0x0800 +/* 802.11n HT capability AMPDU settings */ #define IEEE80211_HT_CAP_AMPDU_FACTOR 0x03 #define IEEE80211_HT_CAP_AMPDU_DENSITY 0x1C +/* 802.11n HT capability MSC set */ +#define IEEE80211_SUPP_MCS_SET_UEQM 4 +#define IEEE80211_HT_CAP_MAX_STREAMS 4 +#define IEEE80211_SUPP_MCS_SET_LEN 10 +/* maximum streams the spec allows */ +#define IEEE80211_HT_CAP_MCS_TX_DEFINED 0x01 +#define IEEE80211_HT_CAP_MCS_TX_RX_DIFF 0x02 +#define IEEE80211_HT_CAP_MCS_TX_STREAMS 0x0C +#define IEEE80211_HT_CAP_MCS_TX_UEQM 0x10 /* 802.11n HT IE masks */ #define IEEE80211_HT_IE_CHA_SEC_OFFSET 0x03 #define IEEE80211_HT_IE_CHA_WIDTH 0x04 @@ -316,10 +326,10 @@ struct ieee80211_ht_addt_info { #define IEEE80211_HT_IE_NON_HT_STA_PRSNT 0x0010 /* MIMO Power Save Modes */ -#define WLAN_HT_CAP_MIMO_PS_STATIC 0 -#define WLAN_HT_CAP_MIMO_PS_DYNAMIC 1 -#define WLAN_HT_CAP_MIMO_PS_INVALID 2 -#define WLAN_HT_CAP_MIMO_PS_DISABLED 3 +#define WLAN_HT_CAP_MIMO_PS_STATIC 0 +#define WLAN_HT_CAP_MIMO_PS_DYNAMIC 1 +#define WLAN_HT_CAP_MIMO_PS_INVALID 2 +#define WLAN_HT_CAP_MIMO_PS_DISABLED 3 /* Authentication algorithms */ #define WLAN_AUTH_OPEN 0 diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 36016363d225..b0fddb7de549 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -35,8 +35,6 @@ #include "debugfs.h" #include "debugfs_netdev.h" -#define SUPP_MCS_SET_LEN 16 - /* * For seeing transmitted packets on monitor interfaces * we have a radiotap header too. @@ -1068,56 +1066,84 @@ u32 ieee80211_handle_ht(struct ieee80211_local *local, int enable_ht, struct ieee80211_supported_band *sband; struct ieee80211_ht_info ht_conf; struct ieee80211_ht_bss_info ht_bss_conf; - int i; u32 changed = 0; + int i; + u8 max_tx_streams = IEEE80211_HT_CAP_MAX_STREAMS; + u8 tx_mcs_set_cap; sband = local->hw.wiphy->bands[conf->channel->band]; + memset(&ht_conf, 0, sizeof(struct ieee80211_ht_info)); + memset(&ht_bss_conf, 0, sizeof(struct ieee80211_ht_bss_info)); + /* HT is not supported */ if (!sband->ht_info.ht_supported) { conf->flags &= ~IEEE80211_CONF_SUPPORT_HT_MODE; - return 0; + goto out; } - memset(&ht_conf, 0, sizeof(struct ieee80211_ht_info)); - memset(&ht_bss_conf, 0, sizeof(struct ieee80211_ht_bss_info)); - - if (enable_ht) { - if (!(conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE)) + /* disable HT */ + if (!enable_ht) { + if (conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) changed |= BSS_CHANGED_HT; + conf->flags &= ~IEEE80211_CONF_SUPPORT_HT_MODE; + conf->ht_conf.ht_supported = 0; + goto out; + } - conf->flags |= IEEE80211_CONF_SUPPORT_HT_MODE; - ht_conf.ht_supported = 1; - ht_conf.cap = req_ht_cap->cap & sband->ht_info.cap; - ht_conf.cap &= ~(IEEE80211_HT_CAP_MIMO_PS); - ht_conf.cap |= sband->ht_info.cap & IEEE80211_HT_CAP_MIMO_PS; + if (!(conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE)) + changed |= BSS_CHANGED_HT; - for (i = 0; i < SUPP_MCS_SET_LEN; i++) - ht_conf.supp_mcs_set[i] = - sband->ht_info.supp_mcs_set[i] & - req_ht_cap->supp_mcs_set[i]; + conf->flags |= IEEE80211_CONF_SUPPORT_HT_MODE; + ht_conf.ht_supported = 1; - ht_bss_conf.primary_channel = req_bss_cap->primary_channel; - ht_bss_conf.bss_cap = req_bss_cap->bss_cap; - ht_bss_conf.bss_op_mode = req_bss_cap->bss_op_mode; + ht_conf.cap = req_ht_cap->cap & sband->ht_info.cap; + ht_conf.cap &= ~(IEEE80211_HT_CAP_MIMO_PS); + ht_conf.cap |= sband->ht_info.cap & IEEE80211_HT_CAP_MIMO_PS; + ht_bss_conf.primary_channel = req_bss_cap->primary_channel; + ht_bss_conf.bss_cap = req_bss_cap->bss_cap; + ht_bss_conf.bss_op_mode = req_bss_cap->bss_op_mode; - ht_conf.ampdu_factor = req_ht_cap->ampdu_factor; - ht_conf.ampdu_density = req_ht_cap->ampdu_density; + ht_conf.ampdu_factor = req_ht_cap->ampdu_factor; + ht_conf.ampdu_density = req_ht_cap->ampdu_density; - /* if bss configuration changed store the new one */ - if (memcmp(&conf->ht_conf, &ht_conf, sizeof(ht_conf)) || - memcmp(&conf->ht_bss_conf, &ht_bss_conf, sizeof(ht_bss_conf))) { - changed |= BSS_CHANGED_HT; - memcpy(&conf->ht_conf, &ht_conf, sizeof(ht_conf)); - memcpy(&conf->ht_bss_conf, &ht_bss_conf, sizeof(ht_bss_conf)); - } - } else { - if (conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) - changed |= BSS_CHANGED_HT; - conf->flags &= ~IEEE80211_CONF_SUPPORT_HT_MODE; - } + /* Bits 96-100 */ + tx_mcs_set_cap = sband->ht_info.supp_mcs_set[12]; + /* configure suppoerted Tx MCS according to requested MCS + * (based in most cases on Rx capabilities of peer) and self + * Tx MCS capabilities (as defined by low level driver HW + * Tx capabilities) */ + if (!(tx_mcs_set_cap & IEEE80211_HT_CAP_MCS_TX_DEFINED)) + goto check_changed; + + /* Counting from 0 therfore + 1 */ + if (tx_mcs_set_cap & IEEE80211_HT_CAP_MCS_TX_RX_DIFF) + max_tx_streams = ((tx_mcs_set_cap & + IEEE80211_HT_CAP_MCS_TX_STREAMS) >> 2) + 1; + + for (i = 0; i < max_tx_streams; i++) + ht_conf.supp_mcs_set[i] = + sband->ht_info.supp_mcs_set[i] & + req_ht_cap->supp_mcs_set[i]; + + if (tx_mcs_set_cap & IEEE80211_HT_CAP_MCS_TX_UEQM) + for (i = IEEE80211_SUPP_MCS_SET_UEQM; + i < IEEE80211_SUPP_MCS_SET_LEN; i++) + ht_conf.supp_mcs_set[i] = + sband->ht_info.supp_mcs_set[i] & + req_ht_cap->supp_mcs_set[i]; + +check_changed: + /* if bss configuration changed store the new one */ + if (memcmp(&conf->ht_conf, &ht_conf, sizeof(ht_conf)) || + memcmp(&conf->ht_bss_conf, &ht_bss_conf, sizeof(ht_bss_conf))) { + changed |= BSS_CHANGED_HT; + memcpy(&conf->ht_conf, &ht_conf, sizeof(ht_conf)); + memcpy(&conf->ht_bss_conf, &ht_bss_conf, sizeof(ht_bss_conf)); + } +out: return changed; } -- cgit v1.2.3 From 57e6983cbde91b4569b4014b933f3a16e12b99fd Mon Sep 17 00:00:00 2001 From: Matt Carlson Date: Sun, 25 May 2008 23:48:31 -0700 Subject: tg3: Add 5785 ASIC revision This patch added the 5785 device ID and ASIC revision to the code. Signed-off-by: Matt Carlson Signed-off-by: Michael Chan Signed-off-by: Benjamin Li Signed-off-by: David S. Miller --- drivers/net/tg3.c | 58 ++++++++++++++++++++++++++++++++++++------------- drivers/net/tg3.h | 1 + include/linux/pci_ids.h | 1 + 3 files changed, 45 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 028276edd3bc..69ded90cd96e 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -204,6 +204,7 @@ static struct pci_device_id tg3_pci_tbl[] = { {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5723)}, {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5761)}, {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5761E)}, + {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5785)}, {PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9DXX)}, {PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9MXX)}, {PCI_DEVICE(PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC1000)}, @@ -5710,7 +5711,8 @@ static int tg3_chip_reset(struct tg3 *tp) GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761) + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) tw32(GRC_FASTBOOT_PC, 0); /* @@ -7004,7 +7006,8 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy) return err; if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5784 && - GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5761) { + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5761 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5785) { /* This value is determined during the probe time DMA * engine test, tg3_test_dma. */ @@ -7243,7 +7246,8 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy) RDMAC_MODE_FIFOURUN_ENAB | RDMAC_MODE_FIFOOREAD_ENAB | RDMAC_MODE_LNGREAD_ENAB); - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784) + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) rdmac_mode |= RDMAC_MODE_BD_SBD_CRPT_ENAB | RDMAC_MODE_MBUF_RBD_CRPT_ENAB | RDMAC_MODE_MBUF_SBD_CRPT_ENAB; @@ -7411,7 +7415,8 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy) if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755) || (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787) || (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784) || - (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761)) + (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761) || + (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785)) val |= WDMAC_MODE_STATUS_TAG_FIX; tw32_f(WDMAC_MODE, val); @@ -7473,7 +7478,9 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy) tp->rx_mode = RX_MODE_ENABLE; if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761) + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) tp->rx_mode |= RX_MODE_IPV6_CSUM_ENABLE; tw32_f(MAC_RX_MODE, tp->rx_mode); @@ -9028,7 +9035,10 @@ static int tg3_set_tso(struct net_device *dev, u32 value) (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906)) { if (value) { dev->features |= NETIF_F_TSO6; - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761) + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || + (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 && + GET_CHIP_REV(tp->pci_chip_rev_id) != CHIPREV_5784_AX) || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) dev->features |= NETIF_F_TSO_ECN; } else dev->features &= ~(NETIF_F_TSO6 | NETIF_F_TSO_ECN); @@ -9286,7 +9296,8 @@ static int tg3_set_tx_csum(struct net_device *dev, u32 data) if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761) + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) ethtool_op_set_tx_ipv6_csum(dev, data); else ethtool_op_set_tx_csum(dev, data); @@ -9807,7 +9818,8 @@ static int tg3_test_memory(struct tg3 *tp) if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761) + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) mem_tbl = mem_tbl_5755; else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) mem_tbl = mem_tbl_5906; @@ -10014,7 +10026,8 @@ static int tg3_test_loopback(struct tg3 *tp) return TG3_LOOPBACK_FAILED; if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761) { + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) { int i; u32 status; @@ -10042,7 +10055,8 @@ static int tg3_test_loopback(struct tg3 *tp) err |= TG3_MAC_LOOPBACK_FAILED; if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761) { + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) { tw32(TG3_CPMU_CTRL, cpmuctrl); /* Release the mutex */ @@ -10690,7 +10704,8 @@ static void __devinit tg3_nvram_init(struct tg3 *tp) else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755) tg3_get_5755_nvram_info(tp); else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784) + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) tg3_get_5787_nvram_info(tp); else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761) tg3_get_5761_nvram_info(tp); @@ -11021,6 +11036,7 @@ static int tg3_nvram_write_block_buffered(struct tg3 *tp, u32 offset, u32 len, (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5787) && (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5784) && (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5761) && + (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5785) && (tp->nvram_jedecnum == JEDEC_ST) && (nvram_cmd & NVRAM_CMD_FIRST)) { @@ -11924,6 +11940,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906 || (tp->tg3_flags2 & TG3_FLG2_5780_CLASS)) tp->tg3_flags2 |= TG3_FLG2_5750_PLUS; @@ -11945,6 +11962,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) { tp->tg3_flags2 |= TG3_FLG2_HW_TSO_2; tp->tg3_flags2 |= TG3_FLG2_1SHOT_MSI; @@ -12147,7 +12165,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) } if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761) { + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) { tp->tg3_flags |= TG3_FLAG_CPMU_PRESENT; if (tp->pci_chip_rev_id == CHIPREV_ID_5784_A0 || @@ -12231,7 +12250,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) tp->tg3_flags2 |= TG3_FLG2_PHY_JITTER_BUG; if (tp->pdev->device == PCI_DEVICE_ID_TIGON3_5755M) tp->tg3_flags2 |= TG3_FLG2_PHY_ADJUST_TRIM; - } else if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906) + } else if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5785) tp->tg3_flags2 |= TG3_FLG2_PHY_BER_BUG; } @@ -12252,6 +12272,9 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) GET_CHIP_REV(tp->pci_chip_rev_id) != CHIPREV_5700_BX) tp->coalesce_mode |= HOSTCC_MODE_32BYTE; + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) + tp->tg3_flags3 |= TG3_FLG3_USE_PHYLIB; + err = tg3_mdio_init(tp); if (err) return err; @@ -12383,6 +12406,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) tp->dev->hard_start_xmit = tg3_start_xmit; else @@ -13307,7 +13331,10 @@ static int __devinit tg3_init_one(struct pci_dev *pdev, if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO_2) && (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906)) dev->features |= NETIF_F_TSO6; - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761) + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || + (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 && + GET_CHIP_REV(tp->pci_chip_rev_id) != CHIPREV_5784_AX) || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) dev->features |= NETIF_F_TSO_ECN; } @@ -13373,7 +13400,8 @@ static int __devinit tg3_init_one(struct pci_dev *pdev, if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5784 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761) + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5761 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) dev->features |= NETIF_F_IPV6_CSUM; tp->tg3_flags |= TG3_FLAG_RX_CHECKSUMS; diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h index 48f45c17f60d..95ac4c9590a1 100644 --- a/drivers/net/tg3.h +++ b/drivers/net/tg3.h @@ -128,6 +128,7 @@ #define ASIC_REV_USE_PROD_ID_REG 0x0f #define ASIC_REV_5784 0x5784 #define ASIC_REV_5761 0x5761 +#define ASIC_REV_5785 0x5785 #define GET_CHIP_REV(CHIP_REV_ID) ((CHIP_REV_ID) >> 8) #define CHIPREV_5700_AX 0x70 #define CHIPREV_5700_BX 0x71 diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index cf6dbd759395..72c038560e7d 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1979,6 +1979,7 @@ #define PCI_DEVICE_ID_TIGON3_5787M 0x1693 #define PCI_DEVICE_ID_TIGON3_5782 0x1696 #define PCI_DEVICE_ID_TIGON3_5784 0x1698 +#define PCI_DEVICE_ID_TIGON3_5785 0x1699 #define PCI_DEVICE_ID_TIGON3_5786 0x169a #define PCI_DEVICE_ID_TIGON3_5787 0x169b #define PCI_DEVICE_ID_TIGON3_5788 0x169c -- cgit v1.2.3 From a9daf36746b1fb5c2db8d164ca70c30c63a0d7b2 Mon Sep 17 00:00:00 2001 From: Matt Carlson Date: Sun, 25 May 2008 23:49:44 -0700 Subject: tg3: Add shmem options. This patch adds some options obtained through shared memory. Signed-off-by: Matt Carlson Signed-off-by: Michael Chan Signed-off-by: Benjamin Li Signed-off-by: David S. Miller --- drivers/net/tg3.c | 95 +++++++++++++++++++++++++++++++++++++++++++++---- drivers/net/tg3.h | 30 +++++++++++++++- include/linux/brcmphy.h | 6 ++++ 3 files changed, 123 insertions(+), 8 deletions(-) create mode 100644 include/linux/brcmphy.h (limited to 'include/linux') diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 69ded90cd96e..365fcffc3144 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -869,6 +870,51 @@ static int tg3_mdio_reset(struct mii_bus *bp) return 0; } +static void tg3_mdio_config(struct tg3 *tp) +{ + u32 val; + + if (tp->mdio_bus.phy_map[PHY_ADDR]->interface != + PHY_INTERFACE_MODE_RGMII) + return; + + val = tr32(MAC_PHYCFG1) & ~(MAC_PHYCFG1_RGMII_EXT_RX_DEC | + MAC_PHYCFG1_RGMII_SND_STAT_EN); + if (tp->tg3_flags3 & TG3_FLG3_RGMII_STD_IBND_DISABLE) { + if (tp->tg3_flags3 & TG3_FLG3_RGMII_EXT_IBND_RX_EN) + val |= MAC_PHYCFG1_RGMII_EXT_RX_DEC; + if (tp->tg3_flags3 & TG3_FLG3_RGMII_EXT_IBND_TX_EN) + val |= MAC_PHYCFG1_RGMII_SND_STAT_EN; + } + tw32(MAC_PHYCFG1, val | MAC_PHYCFG1_RGMII_INT | MAC_PHYCFG1_TXC_DRV); + + val = tr32(MAC_PHYCFG2) & ~(MAC_PHYCFG2_INBAND_ENABLE); + if (!(tp->tg3_flags3 & TG3_FLG3_RGMII_STD_IBND_DISABLE)) + val |= MAC_PHYCFG2_INBAND_ENABLE; + tw32(MAC_PHYCFG2, val); + + val = tr32(MAC_EXT_RGMII_MODE); + val &= ~(MAC_RGMII_MODE_RX_INT_B | + MAC_RGMII_MODE_RX_QUALITY | + MAC_RGMII_MODE_RX_ACTIVITY | + MAC_RGMII_MODE_RX_ENG_DET | + MAC_RGMII_MODE_TX_ENABLE | + MAC_RGMII_MODE_TX_LOWPWR | + MAC_RGMII_MODE_TX_RESET); + if (tp->tg3_flags3 & TG3_FLG3_RGMII_STD_IBND_DISABLE) { + if (tp->tg3_flags3 & TG3_FLG3_RGMII_EXT_IBND_RX_EN) + val |= MAC_RGMII_MODE_RX_INT_B | + MAC_RGMII_MODE_RX_QUALITY | + MAC_RGMII_MODE_RX_ACTIVITY | + MAC_RGMII_MODE_RX_ENG_DET; + if (tp->tg3_flags3 & TG3_FLG3_RGMII_EXT_IBND_TX_EN) + val |= MAC_RGMII_MODE_TX_ENABLE | + MAC_RGMII_MODE_TX_LOWPWR | + MAC_RGMII_MODE_TX_RESET; + } + tw32(MAC_EXT_RGMII_MODE, val); +} + static void tg3_mdio_start(struct tg3 *tp) { if (tp->tg3_flags3 & TG3_FLG3_MDIOBUS_INITED) { @@ -880,6 +926,9 @@ static void tg3_mdio_start(struct tg3 *tp) tp->mi_mode &= ~MAC_MI_MODE_AUTO_POLL; tw32_f(MAC_MI_MODE, tp->mi_mode); udelay(80); + + if (tp->tg3_flags3 & TG3_FLG3_MDIOBUS_INITED) + tg3_mdio_config(tp); } static void tg3_mdio_stop(struct tg3 *tp) @@ -895,6 +944,7 @@ static int tg3_mdio_init(struct tg3 *tp) { int i; u32 reg; + struct phy_device *phydev; struct mii_bus *mdio_bus = &tp->mdio_bus; tg3_mdio_start(tp); @@ -928,13 +978,34 @@ static int tg3_mdio_init(struct tg3 *tp) tg3_bmcr_reset(tp); i = mdiobus_register(mdio_bus); - if (!i) - tp->tg3_flags3 |= TG3_FLG3_MDIOBUS_INITED; - else + if (i) { printk(KERN_WARNING "%s: mdiobus_reg failed (0x%x)\n", tp->dev->name, i); + return i; + } - return i; + tp->tg3_flags3 |= TG3_FLG3_MDIOBUS_INITED; + + phydev = tp->mdio_bus.phy_map[PHY_ADDR]; + + switch (phydev->phy_id) { + case TG3_PHY_ID_BCM50610: + phydev->interface = PHY_INTERFACE_MODE_RGMII; + if (tp->tg3_flags3 & TG3_FLG3_RGMII_STD_IBND_DISABLE) + phydev->dev_flags |= PHY_BRCM_STD_IBND_DISABLE; + if (tp->tg3_flags3 & TG3_FLG3_RGMII_EXT_IBND_RX_EN) + phydev->dev_flags |= PHY_BRCM_EXT_IBND_RX_ENABLE; + if (tp->tg3_flags3 & TG3_FLG3_RGMII_EXT_IBND_TX_EN) + phydev->dev_flags |= PHY_BRCM_EXT_IBND_TX_ENABLE; + break; + case TG3_PHY_ID_BCMAC131: + phydev->interface = PHY_INTERFACE_MODE_MII; + break; + } + + tg3_mdio_config(tp); + + return 0; } static void tg3_mdio_fini(struct tg3 *tp) @@ -1238,8 +1309,8 @@ static int tg3_phy_init(struct tg3 *tp) phydev = tp->mdio_bus.phy_map[PHY_ADDR]; /* Attach the MAC to the PHY. */ - phydev = phy_connect(tp->dev, phydev->dev.bus_id, - tg3_adjust_link, 0, phydev->interface); + phydev = phy_connect(tp->dev, phydev->dev.bus_id, tg3_adjust_link, + phydev->dev_flags, phydev->interface); if (IS_ERR(phydev)) { printk(KERN_ERR "%s: Could not attach to PHY\n", tp->dev->name); return PTR_ERR(phydev); @@ -11219,7 +11290,7 @@ static void __devinit tg3_get_eeprom_hw_cfg(struct tg3 *tp) tg3_read_mem(tp, NIC_SRAM_DATA_SIG, &val); if (val == NIC_SRAM_DATA_SIG_MAGIC) { u32 nic_cfg, led_cfg; - u32 nic_phy_id, ver, cfg2 = 0, eeprom_phy_id; + u32 nic_phy_id, ver, cfg2 = 0, cfg4 = 0, eeprom_phy_id; int eeprom_phy_serdes = 0; tg3_read_mem(tp, NIC_SRAM_DATA_CFG, &nic_cfg); @@ -11233,6 +11304,9 @@ static void __devinit tg3_get_eeprom_hw_cfg(struct tg3 *tp) (ver > 0) && (ver < 0x100)) tg3_read_mem(tp, NIC_SRAM_DATA_CFG_2, &cfg2); + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5785) + tg3_read_mem(tp, NIC_SRAM_DATA_CFG_4, &cfg4); + if ((nic_cfg & NIC_SRAM_DATA_CFG_PHY_TYPE_MASK) == NIC_SRAM_DATA_CFG_PHY_TYPE_FIBER) eeprom_phy_serdes = 1; @@ -11357,6 +11431,13 @@ static void __devinit tg3_get_eeprom_hw_cfg(struct tg3 *tp) if (cfg3 & NIC_SRAM_ASPM_DEBOUNCE) tp->tg3_flags |= TG3_FLAG_ASPM_WORKAROUND; } + + if (cfg4 & NIC_SRAM_RGMII_STD_IBND_DISABLE) + tp->tg3_flags3 |= TG3_FLG3_RGMII_STD_IBND_DISABLE; + if (cfg4 & NIC_SRAM_RGMII_EXT_IBND_RX_EN) + tp->tg3_flags3 |= TG3_FLG3_RGMII_EXT_IBND_RX_EN; + if (cfg4 & NIC_SRAM_RGMII_EXT_IBND_TX_EN) + tp->tg3_flags3 |= TG3_FLG3_RGMII_EXT_IBND_TX_EN; } } diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h index 95ac4c9590a1..df07842172b7 100644 --- a/drivers/net/tg3.h +++ b/drivers/net/tg3.h @@ -529,7 +529,23 @@ #define MAC_SERDES_CFG 0x00000590 #define MAC_SERDES_CFG_EDGE_SELECT 0x00001000 #define MAC_SERDES_STAT 0x00000594 -/* 0x598 --> 0x5b0 unused */ +/* 0x598 --> 0x5a0 unused */ +#define MAC_PHYCFG1 0x000005a0 +#define MAC_PHYCFG1_RGMII_INT 0x00000001 +#define MAC_PHYCFG1_RGMII_EXT_RX_DEC 0x02000000 +#define MAC_PHYCFG1_RGMII_SND_STAT_EN 0x04000000 +#define MAC_PHYCFG1_TXC_DRV 0x20000000 +#define MAC_PHYCFG2 0x000005a4 +#define MAC_PHYCFG2_INBAND_ENABLE 0x00000001 +#define MAC_EXT_RGMII_MODE 0x000005a8 +#define MAC_RGMII_MODE_TX_ENABLE 0x00000001 +#define MAC_RGMII_MODE_TX_LOWPWR 0x00000002 +#define MAC_RGMII_MODE_TX_RESET 0x00000004 +#define MAC_RGMII_MODE_RX_INT_B 0x00000100 +#define MAC_RGMII_MODE_RX_QUALITY 0x00000200 +#define MAC_RGMII_MODE_RX_ACTIVITY 0x00000400 +#define MAC_RGMII_MODE_RX_ENG_DET 0x00000800 +/* 0x5ac --> 0x5b0 unused */ #define SERDES_RX_CTRL 0x000005b0 /* 5780/5714 only */ #define SERDES_RX_SIG_DETECT 0x00000400 #define SG_DIG_CTRL 0x000005b0 @@ -1715,6 +1731,12 @@ #define NIC_SRAM_DATA_CFG_3 0x00000d3c #define NIC_SRAM_ASPM_DEBOUNCE 0x00000002 +#define NIC_SRAM_DATA_CFG_4 0x00000d60 +#define NIC_SRAM_GMII_MODE 0x00000002 +#define NIC_SRAM_RGMII_STD_IBND_DISABLE 0x00000004 +#define NIC_SRAM_RGMII_EXT_IBND_RX_EN 0x00000008 +#define NIC_SRAM_RGMII_EXT_IBND_TX_EN 0x00000010 + #define NIC_SRAM_RX_MINI_BUFFER_DESC 0x00001000 #define NIC_SRAM_DMA_DESC_POOL_BASE 0x00002000 @@ -2486,6 +2508,9 @@ struct tg3 { #define TG3_FLG3_MDIOBUS_INITED 0x00000020 #define TG3_FLG3_MDIOBUS_PAUSED 0x00000040 #define TG3_FLG3_PHY_CONNECTED 0x00000080 +#define TG3_FLG3_RGMII_STD_IBND_DISABLE 0x00000100 +#define TG3_FLG3_RGMII_EXT_IBND_RX_EN 0x00000200 +#define TG3_FLG3_RGMII_EXT_IBND_TX_EN 0x00000400 struct timer_list timer; u16 timer_counter; @@ -2556,6 +2581,9 @@ struct tg3 { #define PHY_REV_BCM5401_B2 0x3 #define PHY_REV_BCM5401_C0 0x6 #define PHY_REV_BCM5411_X0 0x1 /* Found on Netgear GA302T */ +#define TG3_PHY_ID_BCM50610 0x143bd60 +#define TG3_PHY_ID_BCMAC131 0x143bc70 + u32 led_ctrl; u32 phy_otp; diff --git a/include/linux/brcmphy.h b/include/linux/brcmphy.h new file mode 100644 index 000000000000..9b64b6d67873 --- /dev/null +++ b/include/linux/brcmphy.h @@ -0,0 +1,6 @@ +#define PHY_BRCM_WIRESPEED_ENABLE 0x00000001 +#define PHY_BRCM_AUTO_PWRDWN_ENABLE 0x00000002 +#define PHY_BRCM_APD_CLK125_ENABLE 0x00000004 +#define PHY_BRCM_STD_IBND_DISABLE 0x00000008 +#define PHY_BRCM_EXT_IBND_RX_ENABLE 0x00000010 +#define PHY_BRCM_EXT_IBND_TX_ENABLE 0x00000020 -- cgit v1.2.3 From 75a9cd524c6bb441c61c85bae7020ce5e8b2e807 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 29 May 2008 01:43:48 -0700 Subject: wanrouter: Fix ioctl handler declaration. Signed-off-by: David S. Miller --- include/linux/wanrouter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/wanrouter.h b/include/linux/wanrouter.h index 3add87465b1f..e0aa39612eba 100644 --- a/include/linux/wanrouter.h +++ b/include/linux/wanrouter.h @@ -522,7 +522,7 @@ extern int wanrouter_proc_init(void); extern void wanrouter_proc_cleanup(void); extern int wanrouter_proc_add(struct wan_device *wandev); extern int wanrouter_proc_delete(struct wan_device *wandev); -extern int wanrouter_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); +extern long wanrouter_ioctl(struct file *file, unsigned int cmd, unsigned long arg); /* Public Data */ /* list of registered devices */ -- cgit v1.2.3 From b79eeeb9e48457579cb742cd02e162fcd673c4a3 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Thu, 29 May 2008 03:25:23 -0700 Subject: tcp: Reorganize tcp_sock to fill 64-bit holes & improve locality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I tried to group recovery related fields nearby (non-CA_Open related variables, to be more accurate) so that one to three cachelines would not be necessary in CA_Open. These are now contiguously deployed: struct sk_buff_head out_of_order_queue; /* 1968 80 */ /* --- cacheline 32 boundary (2048 bytes) --- */ struct tcp_sack_block duplicate_sack[1]; /* 2048 8 */ struct tcp_sack_block selective_acks[4]; /* 2056 32 */ struct tcp_sack_block recv_sack_cache[4]; /* 2088 32 */ /* --- cacheline 33 boundary (2112 bytes) was 8 bytes ago --- */ struct sk_buff * highest_sack; /* 2120 8 */ int lost_cnt_hint; /* 2128 4 */ int retransmit_cnt_hint; /* 2132 4 */ u32 lost_retrans_low; /* 2136 4 */ u8 reordering; /* 2140 1 */ u8 keepalive_probes; /* 2141 1 */ /* XXX 2 bytes hole, try to pack */ u32 prior_ssthresh; /* 2144 4 */ u32 high_seq; /* 2148 4 */ u32 retrans_stamp; /* 2152 4 */ u32 undo_marker; /* 2156 4 */ int undo_retrans; /* 2160 4 */ u32 total_retrans; /* 2164 4 */ ...and they're then followed by URG slowpath & keepalive related variables. Head of the out_of_order_queue always needed for empty checks, if that's empty (and TCP is in CA_Open), following ~200 bytes (in 64-bit) shouldn't be necessary for anything. If only OFO queue exists but TCP is in CA_Open, selective_acks (and possibly duplicate_sack) are necessary besides the out_of_order_queue but the rest of the block again shouldn't be (ie., the other direction had losses). As the cacheline boundaries depend on many factors in the preceeding stuff, trying to align considering them doesn't make too much sense. Commented one ordering hazard. There are number of low utilized u8/16s that could be combined get 2 bytes less in total so that the hole could be made to vanish (includes at least ecn_flags, urg_data, urg_mode, frto_counter, nonagle). Signed-off-by: Ilpo Järvinen Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/tcp.h | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 18e62e3d406f..9881295f3857 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -296,10 +296,9 @@ struct tcp_sock { u32 rcv_ssthresh; /* Current window clamp */ u32 frto_highmark; /* snd_nxt when RTO occurred */ - u8 reordering; /* Packet reordering metric. */ + u16 advmss; /* Advertised MSS */ u8 frto_counter; /* Number of new acks after RTO */ u8 nonagle; /* Disable Nagle algorithm? */ - u8 keepalive_probes; /* num of allowed keep alive probes */ /* RTT measurement */ u32 srtt; /* smoothed round trip time << 3 */ @@ -310,6 +309,10 @@ struct tcp_sock { u32 packets_out; /* Packets which are "in flight" */ u32 retrans_out; /* Retransmitted packets out */ + + u16 urg_data; /* Saved octet of OOB data and control flags */ + u8 urg_mode; /* In urgent mode */ + u8 ecn_flags; /* ECN status bits. */ /* * Options received (usually on last packet, some only on SYN packets). */ @@ -325,13 +328,24 @@ struct tcp_sock { u32 snd_cwnd_used; u32 snd_cwnd_stamp; - struct sk_buff_head out_of_order_queue; /* Out of order segments go here */ - u32 rcv_wnd; /* Current receiver window */ u32 write_seq; /* Tail(+1) of data held in tcp send buffer */ u32 pushed_seq; /* Last pushed seq, required to talk to windows */ + u32 lost_out; /* Lost packets */ + u32 sacked_out; /* SACK'd packets */ + u32 fackets_out; /* FACK'd packets */ + u32 tso_deferred; + u32 bytes_acked; /* Appropriate Byte Counting - RFC3465 */ -/* SACKs data */ + /* from STCP, retrans queue hinting */ + struct sk_buff* lost_skb_hint; + struct sk_buff *scoreboard_skb_hint; + struct sk_buff *retransmit_skb_hint; + struct sk_buff *forward_skb_hint; + + struct sk_buff_head out_of_order_queue; /* Out of order segments go here */ + + /* SACKs data, these 2 need to be together (see tcp_build_and_update_options) */ struct tcp_sack_block duplicate_sack[1]; /* D-SACK block */ struct tcp_sack_block selective_acks[4]; /* The SACKS themselves*/ @@ -342,23 +356,14 @@ struct tcp_sock { * sacked_out > 0) */ - /* from STCP, retrans queue hinting */ - struct sk_buff* lost_skb_hint; - - struct sk_buff *scoreboard_skb_hint; - struct sk_buff *retransmit_skb_hint; - struct sk_buff *forward_skb_hint; - int lost_cnt_hint; int retransmit_cnt_hint; u32 lost_retrans_low; /* Sent seq after any rxmit (lowest) */ - u16 advmss; /* Advertised MSS */ + u8 reordering; /* Packet reordering metric. */ + u8 keepalive_probes; /* num of allowed keep alive probes */ u32 prior_ssthresh; /* ssthresh saved at recovery start */ - u32 lost_out; /* Lost packets */ - u32 sacked_out; /* SACK'd packets */ - u32 fackets_out; /* FACK'd packets */ u32 high_seq; /* snd_nxt at onset of congestion */ u32 retrans_stamp; /* Timestamp of the last retransmit, @@ -366,25 +371,18 @@ struct tcp_sock { * the first SYN. */ u32 undo_marker; /* tracking retrans started here. */ int undo_retrans; /* number of undoable retransmissions. */ + u32 total_retrans; /* Total retransmits for entire connection */ + u32 urg_seq; /* Seq of received urgent pointer */ - u16 urg_data; /* Saved octet of OOB data and control flags */ - u8 urg_mode; /* In urgent mode */ - u8 ecn_flags; /* ECN status bits. */ u32 snd_up; /* Urgent pointer */ - u32 total_retrans; /* Total retransmits for entire connection */ - u32 bytes_acked; /* Appropriate Byte Counting - RFC3465 */ - unsigned int keepalive_time; /* time before keep alive takes place */ unsigned int keepalive_intvl; /* time interval between keep alive probes */ - int linger2; struct tcp_deferred_accept_info defer_tcp_accept; unsigned long last_synq_overflow; - u32 tso_deferred; - /* Receiver side RTT estimation */ struct { u32 rtt; @@ -412,6 +410,8 @@ struct tcp_sock { /* TCP MD5 Signagure Option information */ struct tcp_md5sig_info *md5sig_info; #endif + + int linger2; }; static inline struct tcp_sock *tcp_sk(const struct sock *sk) -- cgit v1.2.3 From f6d65610df3bd4e7138da03aec391224219df135 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 2 Jun 2008 00:39:45 -0400 Subject: Input: atkbd - use ushort instead of uchar keymap Since some of the keycodes defined in input.h have values greater than 255 we should use unsigned shorts in keymaps. Tested-by: Carlos Corbacho Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/atkbd.c | 20 ++++++++++---------- include/linux/input.h | 2 ++ 2 files changed, 12 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 4a95adc4cc78..56857d1e56d5 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -68,7 +68,7 @@ MODULE_PARM_DESC(extra, "Enable extra LEDs and keys on IBM RapidAcces, EzKey and * are loadable via an userland utility. */ -static unsigned char atkbd_set2_keycode[512] = { +static const unsigned short atkbd_set2_keycode[512] = { #ifdef CONFIG_KEYBOARD_ATKBD_HP_KEYCODES @@ -99,7 +99,7 @@ static unsigned char atkbd_set2_keycode[512] = { #endif }; -static unsigned char atkbd_set3_keycode[512] = { +static const unsigned short atkbd_set3_keycode[512] = { 0, 0, 0, 0, 0, 0, 0, 59, 1,138,128,129,130, 15, 41, 60, 131, 29, 42, 86, 58, 16, 2, 61,133, 56, 44, 31, 30, 17, 3, 62, @@ -115,7 +115,7 @@ static unsigned char atkbd_set3_keycode[512] = { 148,149,147,140 }; -static unsigned char atkbd_unxlate_table[128] = { +static const unsigned short atkbd_unxlate_table[128] = { 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13, 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27, 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42, @@ -161,7 +161,7 @@ static unsigned char atkbd_unxlate_table[128] = { #define ATKBD_SCR_LEFT 249 #define ATKBD_SCR_RIGHT 248 -#define ATKBD_SPECIAL 248 +#define ATKBD_SPECIAL ATKBD_SCR_RIGHT #define ATKBD_LED_EVENT_BIT 0 #define ATKBD_REP_EVENT_BIT 1 @@ -173,7 +173,7 @@ static unsigned char atkbd_unxlate_table[128] = { #define ATKBD_XL_HANGEUL 0x10 #define ATKBD_XL_HANJA 0x20 -static struct { +static const struct { unsigned char keycode; unsigned char set2; } atkbd_scroll_keys[] = { @@ -200,7 +200,7 @@ struct atkbd { char phys[32]; unsigned short id; - unsigned char keycode[512]; + unsigned short keycode[512]; DECLARE_BITMAP(force_release_mask, 512); unsigned char set; unsigned char translated; @@ -357,7 +357,7 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, unsigned int code = data; int scroll = 0, hscroll = 0, click = -1; int value; - unsigned char keycode; + unsigned short keycode; #ifdef ATKBD_DEBUG printk(KERN_DEBUG "atkbd.c: Received %02x flags %02x\n", data, flags); @@ -959,16 +959,16 @@ static void atkbd_set_device_attrs(struct atkbd *atkbd) input_dev->evbit[0] |= BIT_MASK(EV_REL); input_dev->relbit[0] = BIT_MASK(REL_WHEEL) | BIT_MASK(REL_HWHEEL); - set_bit(BTN_MIDDLE, input_dev->keybit); + __set_bit(BTN_MIDDLE, input_dev->keybit); } input_dev->keycode = atkbd->keycode; - input_dev->keycodesize = sizeof(unsigned char); + input_dev->keycodesize = sizeof(unsigned short); input_dev->keycodemax = ARRAY_SIZE(atkbd_set2_keycode); for (i = 0; i < 512; i++) if (atkbd->keycode[i] && atkbd->keycode[i] < ATKBD_SPECIAL) - set_bit(atkbd->keycode[i], input_dev->keybit); + __set_bit(atkbd->keycode[i], input_dev->keybit); } /* diff --git a/include/linux/input.h b/include/linux/input.h index eff711d8a459..af9c6314153b 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -373,6 +373,8 @@ struct input_absinfo { #define KEY_WIMAX 246 +/* Range 248 - 255 is reserved for special needs of AT keyboard driver */ + #define BTN_MISC 0x100 #define BTN_0 0x100 #define BTN_1 0x101 -- cgit v1.2.3 From 9306102ea5696a3815f8d24ac0c0fbd1e19be7d3 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 29 May 2008 16:35:23 +0800 Subject: mac80211: allow disable FAT in specific configurations This patch allows to disable FAT channel in specific configurations. For example the configuration (8, +1), (primary channel 8, extension channel 12) isn't permitted in U.S., but (8, -1), (primary channel 8, extension channel 4) is. When FAT channel configuration is not permitted, FAT channel should be reported as not supported in the capabilities of the HT IE in association request. And sssociation is performed on 20Mhz channel. Signed-off-by: Emmanuel Grumbach Signed-off-by: Tomas Winkler Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 2 ++ include/net/wireless.h | 6 ++++++ net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/mlme.c | 46 ++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 54 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 3c2ac0c37aa8..9300f37cd7e8 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -320,6 +320,8 @@ struct ieee80211_ht_addt_info { #define IEEE80211_HT_CAP_MCS_TX_UEQM 0x10 /* 802.11n HT IE masks */ #define IEEE80211_HT_IE_CHA_SEC_OFFSET 0x03 +#define IEEE80211_HT_IE_CHA_SEC_ABOVE 0x01 +#define IEEE80211_HT_IE_CHA_SEC_BELOW 0x03 #define IEEE80211_HT_IE_CHA_WIDTH 0x04 #define IEEE80211_HT_IE_HT_PROTECTION 0x0003 #define IEEE80211_HT_IE_NON_GF_STA_PRSNT 0x0004 diff --git a/include/net/wireless.h b/include/net/wireless.h index 667b4080d30f..9324f8dd183e 100644 --- a/include/net/wireless.h +++ b/include/net/wireless.h @@ -39,12 +39,18 @@ enum ieee80211_band { * on this channel. * @IEEE80211_CHAN_NO_IBSS: IBSS is not allowed on this channel. * @IEEE80211_CHAN_RADAR: Radar detection is required on this channel. + * @IEEE80211_CHAN_NO_FAT_ABOVE: extension channel above this channel + * is not permitted. + * @IEEE80211_CHAN_NO_FAT_BELOW: extension channel below this channel + * is not permitted. */ enum ieee80211_channel_flags { IEEE80211_CHAN_DISABLED = 1<<0, IEEE80211_CHAN_PASSIVE_SCAN = 1<<1, IEEE80211_CHAN_NO_IBSS = 1<<2, IEEE80211_CHAN_RADAR = 1<<3, + IEEE80211_CHAN_NO_FAT_ABOVE = 1<<4, + IEEE80211_CHAN_NO_FAT_BELOW = 1<<5, }; /** diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 3f8601cafffb..432011cd3647 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -92,6 +92,8 @@ struct ieee80211_sta_bss { size_t wmm_ie_len; u8 *ht_ie; size_t ht_ie_len; + u8 *ht_add_ie; + size_t ht_add_ie_len; #ifdef CONFIG_MAC80211_MESH u8 *mesh_id; size_t mesh_id_len; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 6faa7006681a..d30c11337b04 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -808,8 +808,29 @@ static void ieee80211_send_assoc(struct net_device *dev, /* wmm support is a must to HT */ if (wmm && (ifsta->flags & IEEE80211_STA_WMM_ENABLED) && - sband->ht_info.ht_supported) { - __le16 tmp = cpu_to_le16(sband->ht_info.cap); + sband->ht_info.ht_supported && bss->ht_add_ie) { + struct ieee80211_ht_addt_info *ht_add_info = + (struct ieee80211_ht_addt_info *)bss->ht_add_ie; + u16 cap = sband->ht_info.cap; + __le16 tmp; + u32 flags = local->hw.conf.channel->flags; + + switch (ht_add_info->ht_param & IEEE80211_HT_IE_CHA_SEC_OFFSET) { + case IEEE80211_HT_IE_CHA_SEC_ABOVE: + if (flags & IEEE80211_CHAN_NO_FAT_ABOVE) { + cap &= ~IEEE80211_HT_CAP_SUP_WIDTH; + cap &= ~IEEE80211_HT_CAP_SGI_40; + } + break; + case IEEE80211_HT_IE_CHA_SEC_BELOW: + if (flags & IEEE80211_CHAN_NO_FAT_BELOW) { + cap &= ~IEEE80211_HT_CAP_SUP_WIDTH; + cap &= ~IEEE80211_HT_CAP_SGI_40; + } + break; + } + + tmp = cpu_to_le16(cap); pos = skb_put(skb, sizeof(struct ieee80211_ht_cap)+2); *pos++ = WLAN_EID_HT_CAPABILITY; *pos++ = sizeof(struct ieee80211_ht_cap); @@ -2264,6 +2285,7 @@ static void ieee80211_rx_bss_free(struct ieee80211_sta_bss *bss) kfree(bss->rsn_ie); kfree(bss->wmm_ie); kfree(bss->ht_ie); + kfree(bss->ht_add_ie); kfree(bss_mesh_id(bss)); kfree(bss_mesh_cfg(bss)); kfree(bss); @@ -2640,6 +2662,26 @@ static void ieee80211_rx_bss_info(struct net_device *dev, bss->ht_ie_len = 0; } + if (elems.ht_info_elem && + (!bss->ht_add_ie || + bss->ht_add_ie_len != elems.ht_info_elem_len || + memcmp(bss->ht_add_ie, elems.ht_info_elem, + elems.ht_info_elem_len))) { + kfree(bss->ht_add_ie); + bss->ht_add_ie = + kmalloc(elems.ht_info_elem_len + 2, GFP_ATOMIC); + if (bss->ht_add_ie) { + memcpy(bss->ht_add_ie, elems.ht_info_elem - 2, + elems.ht_info_elem_len + 2); + bss->ht_add_ie_len = elems.ht_info_elem_len + 2; + } else + bss->ht_add_ie_len = 0; + } else if (!elems.ht_info_elem && bss->ht_add_ie) { + kfree(bss->ht_add_ie); + bss->ht_add_ie = NULL; + bss->ht_add_ie_len = 0; + } + bss->beacon_int = le16_to_cpu(mgmt->u.beacon.beacon_int); bss->capability = le16_to_cpu(mgmt->u.beacon.capab_info); -- cgit v1.2.3 From ba569b4c68f11906da2996ee252bcff0df61cb90 Mon Sep 17 00:00:00 2001 From: Masakazu Mokuno Date: Fri, 30 May 2008 16:52:23 +0900 Subject: WEXT: Add support for passing PMK and capability flags to WEXT This defines the flags for setting the PMK to the driver and the capability flag for this so that the user space program can figure out whether the target driver wants to do 4-way hand shake by itself and pass the PMK which is needed before 4-way handshake to the driver. Signed-off-by: Masakazu Mokuno Signed-off-by: John W. Linville --- include/linux/wireless.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/wireless.h b/include/linux/wireless.h index 0a9b5b41ed67..4a95a0e5eeca 100644 --- a/include/linux/wireless.h +++ b/include/linux/wireless.h @@ -611,6 +611,7 @@ #define IW_ENCODE_ALG_WEP 1 #define IW_ENCODE_ALG_TKIP 2 #define IW_ENCODE_ALG_CCMP 3 +#define IW_ENCODE_ALG_PMK 4 /* struct iw_encode_ext ->ext_flags */ #define IW_ENCODE_EXT_TX_SEQ_VALID 0x00000001 #define IW_ENCODE_EXT_RX_SEQ_VALID 0x00000002 @@ -630,6 +631,7 @@ #define IW_ENC_CAPA_WPA2 0x00000002 #define IW_ENC_CAPA_CIPHER_TKIP 0x00000004 #define IW_ENC_CAPA_CIPHER_CCMP 0x00000008 +#define IW_ENC_CAPA_4WAY_HANDSHAKE 0x00000010 /* Event capability macros - in (struct iw_range *)->event_capa * Because we have more than 32 possible events, we use an array of -- cgit v1.2.3 From 9457afee85e0dfc2b5075a391d6f34463b4c2b90 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Thu, 5 Jun 2008 11:23:39 -0700 Subject: netlink: Remove nonblock parameter from netlink_attachskb Signed-off-by: Denis V. Lunev Signed-off-by: David S. Miller --- include/linux/netlink.h | 2 +- ipc/mqueue.c | 2 +- net/netlink/af_netlink.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netlink.h b/include/linux/netlink.h index bec1062a25a1..9ff1b54908f3 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -193,7 +193,7 @@ extern int netlink_unregister_notifier(struct notifier_block *nb); /* finegrained unicast helpers: */ struct sock *netlink_getsockbyfilp(struct file *filp); -int netlink_attachskb(struct sock *sk, struct sk_buff *skb, int nonblock, +int netlink_attachskb(struct sock *sk, struct sk_buff *skb, long *timeo, struct sock *ssk); void netlink_detachskb(struct sock *sk, struct sk_buff *skb); int netlink_sendskb(struct sock *sk, struct sk_buff *skb); diff --git a/ipc/mqueue.c b/ipc/mqueue.c index b3b69fd51330..3e84b958186b 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -1054,7 +1054,7 @@ retry: } timeo = MAX_SCHEDULE_TIMEOUT; - ret = netlink_attachskb(sock, nc, 0, &timeo, NULL); + ret = netlink_attachskb(sock, nc, &timeo, NULL); if (ret == 1) goto retry; if (ret) { diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 9b97f8006c9c..6507c02dbe0d 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -759,7 +759,7 @@ struct sock *netlink_getsockbyfilp(struct file *filp) * 0: continue * 1: repeat lookup - reference dropped while waiting for socket memory. */ -int netlink_attachskb(struct sock *sk, struct sk_buff *skb, int nonblock, +int netlink_attachskb(struct sock *sk, struct sk_buff *skb, long *timeo, struct sock *ssk) { struct netlink_sock *nlk; @@ -892,7 +892,7 @@ retry: return err; } - err = netlink_attachskb(sk, skb, nonblock, &timeo, ssk); + err = netlink_attachskb(sk, skb, &timeo, ssk); if (err == 1) goto retry; if (err) -- cgit v1.2.3 From 93f65158723ceb7078ee9a0fd4830c0de00f4b9e Mon Sep 17 00:00:00 2001 From: Kuo-lang Tseng Date: Mon, 9 Jun 2008 15:55:45 -0700 Subject: netfilter: ebtables: add IPv6 support It implements matching functions for IPv6 address & traffic class (merged from the patch sent by Jan Engelhardt [jengelh@computergmbh.de] http://marc.info/?l=netfilter-devel&m=120182168424052&w=2), protocol, and layer-4 port id. Corresponding watcher logging function is also added for IPv6. Signed-off-by: Kuo-lang Tseng Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netfilter_bridge/ebt_ip6.h | 40 +++++++++ include/linux/netfilter_bridge/ebt_log.h | 3 +- net/bridge/netfilter/Kconfig | 9 ++ net/bridge/netfilter/Makefile | 1 + net/bridge/netfilter/ebt_ip6.c | 144 +++++++++++++++++++++++++++++++ net/bridge/netfilter/ebt_log.c | 64 ++++++++++---- 6 files changed, 244 insertions(+), 17 deletions(-) create mode 100644 include/linux/netfilter_bridge/ebt_ip6.h create mode 100644 net/bridge/netfilter/ebt_ip6.c (limited to 'include/linux') diff --git a/include/linux/netfilter_bridge/ebt_ip6.h b/include/linux/netfilter_bridge/ebt_ip6.h new file mode 100644 index 000000000000..2273c3ae33ca --- /dev/null +++ b/include/linux/netfilter_bridge/ebt_ip6.h @@ -0,0 +1,40 @@ +/* + * ebt_ip6 + * + * Authors: + * Kuo-Lang Tseng + * Manohar Castelino + * + * Jan 11, 2008 + * + */ + +#ifndef __LINUX_BRIDGE_EBT_IP6_H +#define __LINUX_BRIDGE_EBT_IP6_H + +#define EBT_IP6_SOURCE 0x01 +#define EBT_IP6_DEST 0x02 +#define EBT_IP6_TCLASS 0x04 +#define EBT_IP6_PROTO 0x08 +#define EBT_IP6_SPORT 0x10 +#define EBT_IP6_DPORT 0x20 +#define EBT_IP6_MASK (EBT_IP6_SOURCE | EBT_IP6_DEST | EBT_IP6_TCLASS |\ + EBT_IP6_PROTO | EBT_IP6_SPORT | EBT_IP6_DPORT) +#define EBT_IP6_MATCH "ip6" + +/* the same values are used for the invflags */ +struct ebt_ip6_info +{ + struct in6_addr saddr; + struct in6_addr daddr; + struct in6_addr smsk; + struct in6_addr dmsk; + uint8_t tclass; + uint8_t protocol; + uint8_t bitmask; + uint8_t invflags; + uint16_t sport[2]; + uint16_t dport[2]; +}; + +#endif diff --git a/include/linux/netfilter_bridge/ebt_log.h b/include/linux/netfilter_bridge/ebt_log.h index 96e231ae7554..b76e653157e5 100644 --- a/include/linux/netfilter_bridge/ebt_log.h +++ b/include/linux/netfilter_bridge/ebt_log.h @@ -4,7 +4,8 @@ #define EBT_LOG_IP 0x01 /* if the frame is made by ip, log the ip information */ #define EBT_LOG_ARP 0x02 #define EBT_LOG_NFLOG 0x04 -#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP) +#define EBT_LOG_IP6 0x08 +#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP | EBT_LOG_IP6) #define EBT_LOG_PREFIX_SIZE 30 #define EBT_LOG_WATCHER "log" diff --git a/net/bridge/netfilter/Kconfig b/net/bridge/netfilter/Kconfig index 7beeefa0f9c0..fb684c2ff8b6 100644 --- a/net/bridge/netfilter/Kconfig +++ b/net/bridge/netfilter/Kconfig @@ -83,6 +83,15 @@ config BRIDGE_EBT_IP To compile it as a module, choose M here. If unsure, say N. +config BRIDGE_EBT_IP6 + tristate "ebt: IP6 filter support" + depends on BRIDGE_NF_EBTABLES + help + This option adds the IP6 match, which allows basic IPV6 header field + filtering. + + To compile it as a module, choose M here. If unsure, say N. + config BRIDGE_EBT_LIMIT tristate "ebt: limit match support" depends on BRIDGE_NF_EBTABLES diff --git a/net/bridge/netfilter/Makefile b/net/bridge/netfilter/Makefile index 83715d73a503..dd960645b413 100644 --- a/net/bridge/netfilter/Makefile +++ b/net/bridge/netfilter/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_BRIDGE_EBT_802_3) += ebt_802_3.o obj-$(CONFIG_BRIDGE_EBT_AMONG) += ebt_among.o obj-$(CONFIG_BRIDGE_EBT_ARP) += ebt_arp.o obj-$(CONFIG_BRIDGE_EBT_IP) += ebt_ip.o +obj-$(CONFIG_BRIDGE_EBT_IP) += ebt_ip6.o obj-$(CONFIG_BRIDGE_EBT_LIMIT) += ebt_limit.o obj-$(CONFIG_BRIDGE_EBT_MARK) += ebt_mark_m.o obj-$(CONFIG_BRIDGE_EBT_PKTTYPE) += ebt_pkttype.o diff --git a/net/bridge/netfilter/ebt_ip6.c b/net/bridge/netfilter/ebt_ip6.c new file mode 100644 index 000000000000..36efb3a75249 --- /dev/null +++ b/net/bridge/netfilter/ebt_ip6.c @@ -0,0 +1,144 @@ +/* + * ebt_ip6 + * + * Authors: + * Manohar Castelino + * Kuo-Lang Tseng + * Jan Engelhardt + * + * Summary: + * This is just a modification of the IPv4 code written by + * Bart De Schuymer + * with the changes required to support IPv6 + * + * Jan, 2008 + */ + +#include +#include +#include +#include +#include +#include +#include + +struct tcpudphdr { + __be16 src; + __be16 dst; +}; + +static int ebt_filter_ip6(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, const void *data, + unsigned int datalen) +{ + const struct ebt_ip6_info *info = (struct ebt_ip6_info *)data; + const struct ipv6hdr *ih6; + struct ipv6hdr _ip6h; + const struct tcpudphdr *pptr; + struct tcpudphdr _ports; + struct in6_addr tmp_addr; + int i; + + ih6 = skb_header_pointer(skb, 0, sizeof(_ip6h), &_ip6h); + if (ih6 == NULL) + return EBT_NOMATCH; + if (info->bitmask & EBT_IP6_TCLASS && + FWINV(info->tclass != ipv6_get_dsfield(ih6), EBT_IP6_TCLASS)) + return EBT_NOMATCH; + for (i = 0; i < 4; i++) + tmp_addr.in6_u.u6_addr32[i] = ih6->saddr.in6_u.u6_addr32[i] & + info->smsk.in6_u.u6_addr32[i]; + if (info->bitmask & EBT_IP6_SOURCE && + FWINV((ipv6_addr_cmp(&tmp_addr, &info->saddr) != 0), + EBT_IP6_SOURCE)) + return EBT_NOMATCH; + for (i = 0; i < 4; i++) + tmp_addr.in6_u.u6_addr32[i] = ih6->daddr.in6_u.u6_addr32[i] & + info->dmsk.in6_u.u6_addr32[i]; + if (info->bitmask & EBT_IP6_DEST && + FWINV((ipv6_addr_cmp(&tmp_addr, &info->daddr) != 0), EBT_IP6_DEST)) + return EBT_NOMATCH; + if (info->bitmask & EBT_IP6_PROTO) { + uint8_t nexthdr = ih6->nexthdr; + int offset_ph; + + offset_ph = ipv6_skip_exthdr(skb, sizeof(_ip6h), &nexthdr); + if (offset_ph == -1) + return EBT_NOMATCH; + if (FWINV(info->protocol != nexthdr, EBT_IP6_PROTO)) + return EBT_NOMATCH; + if (!(info->bitmask & EBT_IP6_DPORT) && + !(info->bitmask & EBT_IP6_SPORT)) + return EBT_MATCH; + pptr = skb_header_pointer(skb, offset_ph, sizeof(_ports), + &_ports); + if (pptr == NULL) + return EBT_NOMATCH; + if (info->bitmask & EBT_IP6_DPORT) { + u32 dst = ntohs(pptr->dst); + if (FWINV(dst < info->dport[0] || + dst > info->dport[1], EBT_IP6_DPORT)) + return EBT_NOMATCH; + } + if (info->bitmask & EBT_IP6_SPORT) { + u32 src = ntohs(pptr->src); + if (FWINV(src < info->sport[0] || + src > info->sport[1], EBT_IP6_SPORT)) + return EBT_NOMATCH; + } + return EBT_MATCH; + } + return EBT_MATCH; +} + +static int ebt_ip6_check(const char *tablename, unsigned int hookmask, + const struct ebt_entry *e, void *data, unsigned int datalen) +{ + struct ebt_ip6_info *info = (struct ebt_ip6_info *)data; + + if (datalen != EBT_ALIGN(sizeof(struct ebt_ip6_info))) + return -EINVAL; + if (e->ethproto != htons(ETH_P_IPV6) || e->invflags & EBT_IPROTO) + return -EINVAL; + if (info->bitmask & ~EBT_IP6_MASK || info->invflags & ~EBT_IP6_MASK) + return -EINVAL; + if (info->bitmask & (EBT_IP6_DPORT | EBT_IP6_SPORT)) { + if (info->invflags & EBT_IP6_PROTO) + return -EINVAL; + if (info->protocol != IPPROTO_TCP && + info->protocol != IPPROTO_UDP && + info->protocol != IPPROTO_UDPLITE && + info->protocol != IPPROTO_SCTP && + info->protocol != IPPROTO_DCCP) + return -EINVAL; + } + if (info->bitmask & EBT_IP6_DPORT && info->dport[0] > info->dport[1]) + return -EINVAL; + if (info->bitmask & EBT_IP6_SPORT && info->sport[0] > info->sport[1]) + return -EINVAL; + return 0; +} + +static struct ebt_match filter_ip6 = +{ + .name = EBT_IP6_MATCH, + .match = ebt_filter_ip6, + .check = ebt_ip6_check, + .me = THIS_MODULE, +}; + +static int __init ebt_ip6_init(void) +{ + return ebt_register_match(&filter_ip6); +} + +static void __exit ebt_ip6_fini(void) +{ + ebt_unregister_match(&filter_ip6); +} + +module_init(ebt_ip6_init); +module_exit(ebt_ip6_fini); +MODULE_DESCRIPTION("Ebtables: IPv6 protocol packet match"); +MODULE_LICENSE("GPL"); diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c index 0b209e4aad0a..c883ec8a28b4 100644 --- a/net/bridge/netfilter/ebt_log.c +++ b/net/bridge/netfilter/ebt_log.c @@ -18,6 +18,9 @@ #include #include #include +#include +#include +#include static DEFINE_SPINLOCK(ebt_log_lock); @@ -58,6 +61,27 @@ static void print_MAC(const unsigned char *p) printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':'); } +static void +print_ports(const struct sk_buff *skb, uint8_t protocol, int offset) +{ + if (protocol == IPPROTO_TCP || + protocol == IPPROTO_UDP || + protocol == IPPROTO_UDPLITE || + protocol == IPPROTO_SCTP || + protocol == IPPROTO_DCCP) { + const struct tcpudphdr *pptr; + struct tcpudphdr _ports; + + pptr = skb_header_pointer(skb, offset, + sizeof(_ports), &_ports); + if (pptr == NULL) { + printk(" INCOMPLETE TCP/UDP header"); + return; + } + printk(" SPT=%u DPT=%u", ntohs(pptr->src), ntohs(pptr->dst)); + } +} + #define myNIPQUAD(a) a[0], a[1], a[2], a[3] static void ebt_log_packet(unsigned int pf, unsigned int hooknum, @@ -95,23 +119,31 @@ ebt_log_packet(unsigned int pf, unsigned int hooknum, printk(" IP SRC=%u.%u.%u.%u IP DST=%u.%u.%u.%u, IP " "tos=0x%02X, IP proto=%d", NIPQUAD(ih->saddr), NIPQUAD(ih->daddr), ih->tos, ih->protocol); - if (ih->protocol == IPPROTO_TCP || - ih->protocol == IPPROTO_UDP || - ih->protocol == IPPROTO_UDPLITE || - ih->protocol == IPPROTO_SCTP || - ih->protocol == IPPROTO_DCCP) { - const struct tcpudphdr *pptr; - struct tcpudphdr _ports; - - pptr = skb_header_pointer(skb, ih->ihl*4, - sizeof(_ports), &_ports); - if (pptr == NULL) { - printk(" INCOMPLETE TCP/UDP header"); - goto out; - } - printk(" SPT=%u DPT=%u", ntohs(pptr->src), - ntohs(pptr->dst)); + print_ports(skb, ih->protocol, ih->ihl*4); + goto out; + } + + if ((bitmask & EBT_LOG_IP6) && eth_hdr(skb)->h_proto == + htons(ETH_P_IPV6)) { + const struct ipv6hdr *ih; + struct ipv6hdr _iph; + uint8_t nexthdr; + int offset_ph; + + ih = skb_header_pointer(skb, 0, sizeof(_iph), &_iph); + if (ih == NULL) { + printk(" INCOMPLETE IPv6 header"); + goto out; } + printk(" IPv6 SRC=%x:%x:%x:%x:%x:%x:%x:%x " + "IPv6 DST=%x:%x:%x:%x:%x:%x:%x:%x, IPv6 " + "priority=0x%01X, Next Header=%d", NIP6(ih->saddr), + NIP6(ih->daddr), ih->priority, ih->nexthdr); + nexthdr = ih->nexthdr; + offset_ph = ipv6_skip_exthdr(skb, sizeof(_iph), &nexthdr); + if (offset_ph == -1) + goto out; + print_ports(skb, nexthdr, offset_ph); goto out; } -- cgit v1.2.3 From a258860e01b80e8f554a4ab1a6c95e6042eb8b73 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 9 Jun 2008 15:56:39 -0700 Subject: netfilter: ctnetlink: add full support for SCTP to ctnetlink This patch adds full support for SCTP to ctnetlink. This includes three new attributes: state, original vtag and reply vtag. Signed-off-by: Pablo Neira Ayuso Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netfilter/nfnetlink_conntrack.h | 10 ++++ net/netfilter/nf_conntrack_proto_sctp.c | 80 +++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) (limited to 'include/linux') diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux/netfilter/nfnetlink_conntrack.h index 0a383ac083cb..759bc043dc65 100644 --- a/include/linux/netfilter/nfnetlink_conntrack.h +++ b/include/linux/netfilter/nfnetlink_conntrack.h @@ -81,6 +81,7 @@ enum ctattr_protoinfo { CTA_PROTOINFO_UNSPEC, CTA_PROTOINFO_TCP, CTA_PROTOINFO_DCCP, + CTA_PROTOINFO_SCTP, __CTA_PROTOINFO_MAX }; #define CTA_PROTOINFO_MAX (__CTA_PROTOINFO_MAX - 1) @@ -103,6 +104,15 @@ enum ctattr_protoinfo_dccp { }; #define CTA_PROTOINFO_DCCP_MAX (__CTA_PROTOINFO_DCCP_MAX - 1) +enum ctattr_protoinfo_sctp { + CTA_PROTOINFO_SCTP_UNSPEC, + CTA_PROTOINFO_SCTP_STATE, + CTA_PROTOINFO_SCTP_VTAG_ORIGINAL, + CTA_PROTOINFO_SCTP_VTAG_REPLY, + __CTA_PROTOINFO_SCTP_MAX +}; +#define CTA_PROTOINFO_SCTP_MAX (__CTA_PROTOINFO_SCTP_MAX - 1) + enum ctattr_counters { CTA_COUNTERS_UNSPEC, CTA_COUNTERS_PACKETS, /* old 64bit counters */ diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index cbf2e27a22b2..41183a4d2d62 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -463,6 +463,82 @@ static bool sctp_new(struct nf_conn *ct, const struct sk_buff *skb, return true; } +#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) + +#include +#include + +static int sctp_to_nlattr(struct sk_buff *skb, struct nlattr *nla, + const struct nf_conn *ct) +{ + struct nlattr *nest_parms; + + read_lock_bh(&sctp_lock); + nest_parms = nla_nest_start(skb, CTA_PROTOINFO_SCTP | NLA_F_NESTED); + if (!nest_parms) + goto nla_put_failure; + + NLA_PUT_U8(skb, CTA_PROTOINFO_SCTP_STATE, ct->proto.sctp.state); + + NLA_PUT_BE32(skb, + CTA_PROTOINFO_SCTP_VTAG_ORIGINAL, + htonl(ct->proto.sctp.vtag[IP_CT_DIR_ORIGINAL])); + + NLA_PUT_BE32(skb, + CTA_PROTOINFO_SCTP_VTAG_REPLY, + htonl(ct->proto.sctp.vtag[IP_CT_DIR_REPLY])); + + read_unlock_bh(&sctp_lock); + + nla_nest_end(skb, nest_parms); + + return 0; + +nla_put_failure: + read_unlock_bh(&sctp_lock); + return -1; +} + +static const struct nla_policy sctp_nla_policy[CTA_PROTOINFO_SCTP_MAX+1] = { + [CTA_PROTOINFO_SCTP_STATE] = { .type = NLA_U8 }, + [CTA_PROTOINFO_SCTP_VTAG_ORIGINAL] = { .type = NLA_U32 }, + [CTA_PROTOINFO_SCTP_VTAG_REPLY] = { .type = NLA_U32 }, +}; + +static int nlattr_to_sctp(struct nlattr *cda[], struct nf_conn *ct) +{ + struct nlattr *attr = cda[CTA_PROTOINFO_SCTP]; + struct nlattr *tb[CTA_PROTOINFO_SCTP_MAX+1]; + int err; + + /* updates may not contain the internal protocol info, skip parsing */ + if (!attr) + return 0; + + err = nla_parse_nested(tb, + CTA_PROTOINFO_SCTP_MAX, + attr, + sctp_nla_policy); + if (err < 0) + return err; + + if (!tb[CTA_PROTOINFO_SCTP_STATE] || + !tb[CTA_PROTOINFO_SCTP_VTAG_ORIGINAL] || + !tb[CTA_PROTOINFO_SCTP_VTAG_REPLY]) + return -EINVAL; + + write_lock_bh(&sctp_lock); + ct->proto.sctp.state = nla_get_u8(tb[CTA_PROTOINFO_SCTP_STATE]); + ct->proto.sctp.vtag[IP_CT_DIR_ORIGINAL] = + ntohl(nla_get_be32(tb[CTA_PROTOINFO_SCTP_VTAG_ORIGINAL])); + ct->proto.sctp.vtag[IP_CT_DIR_REPLY] = + ntohl(nla_get_be32(tb[CTA_PROTOINFO_SCTP_VTAG_REPLY])); + write_unlock_bh(&sctp_lock); + + return 0; +} +#endif + #ifdef CONFIG_SYSCTL static unsigned int sctp_sysctl_table_users; static struct ctl_table_header *sctp_sysctl_header; @@ -591,6 +667,8 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp4 __read_mostly = { .new = sctp_new, .me = THIS_MODULE, #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) + .to_nlattr = sctp_to_nlattr, + .from_nlattr = nlattr_to_sctp, .tuple_to_nlattr = nf_ct_port_tuple_to_nlattr, .nlattr_to_tuple = nf_ct_port_nlattr_to_tuple, .nla_policy = nf_ct_port_nla_policy, @@ -617,6 +695,8 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp6 __read_mostly = { .new = sctp_new, .me = THIS_MODULE, #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) + .to_nlattr = sctp_to_nlattr, + .from_nlattr = nlattr_to_sctp, .tuple_to_nlattr = nf_ct_port_tuple_to_nlattr, .nlattr_to_tuple = nf_ct_port_nlattr_to_tuple, .nla_policy = nf_ct_port_nla_policy, -- cgit v1.2.3 From 560ee653b67074b805f1b661988a72a0e58811a5 Mon Sep 17 00:00:00 2001 From: James Morris Date: Mon, 9 Jun 2008 15:57:24 -0700 Subject: netfilter: ip_tables: add iptables security table for mandatory access control rules The following patch implements a new "security" table for iptables, so that MAC (SELinux etc.) networking rules can be managed separately to standard DAC rules. This is to help with distro integration of the new secmark-based network controls, per various previous discussions. The need for a separate table arises from the fact that existing tools and usage of iptables will likely clash with centralized MAC policy management. The SECMARK and CONNSECMARK targets will still be valid in the mangle table to prevent breakage of existing users. Signed-off-by: James Morris Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netfilter_ipv4.h | 1 + include/net/netns/ipv4.h | 1 + net/ipv4/netfilter/Kconfig | 12 +++ net/ipv4/netfilter/Makefile | 1 + net/ipv4/netfilter/iptable_security.c | 180 ++++++++++++++++++++++++++++++++++ net/netfilter/xt_CONNSECMARK.c | 10 +- net/netfilter/xt_SECMARK.c | 10 +- 7 files changed, 209 insertions(+), 6 deletions(-) create mode 100644 net/ipv4/netfilter/iptable_security.c (limited to 'include/linux') diff --git a/include/linux/netfilter_ipv4.h b/include/linux/netfilter_ipv4.h index 650318b0c405..29c7727ff0e8 100644 --- a/include/linux/netfilter_ipv4.h +++ b/include/linux/netfilter_ipv4.h @@ -60,6 +60,7 @@ enum nf_ip_hook_priorities { NF_IP_PRI_MANGLE = -150, NF_IP_PRI_NAT_DST = -100, NF_IP_PRI_FILTER = 0, + NF_IP_PRI_SECURITY = 50, NF_IP_PRI_NAT_SRC = 100, NF_IP_PRI_SELINUX_LAST = 225, NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX, diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 34ee348a2cf2..6ef90b5fafb3 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -36,6 +36,7 @@ struct netns_ipv4 { struct xt_table *iptable_mangle; struct xt_table *iptable_raw; struct xt_table *arptable_filter; + struct xt_table *iptable_security; #endif int sysctl_icmp_echo_ignore_all; diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index 2767841a8cef..6e251402506e 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig @@ -365,6 +365,18 @@ config IP_NF_RAW If you want to compile it as a module, say M here and read . If unsure, say `N'. +# security table for MAC policy +config IP_NF_SECURITY + tristate "Security table" + depends on IP_NF_IPTABLES + depends on SECURITY + default m if NETFILTER_ADVANCED=n + help + This option adds a `security' table to iptables, for use + with Mandatory Access Control (MAC) policy. + + If unsure, say N. + # ARP tables config IP_NF_ARPTABLES tristate "ARP tables support" diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index d9b92fbf5579..3f31291f37ce 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_IP_NF_FILTER) += iptable_filter.o obj-$(CONFIG_IP_NF_MANGLE) += iptable_mangle.o obj-$(CONFIG_NF_NAT) += iptable_nat.o obj-$(CONFIG_IP_NF_RAW) += iptable_raw.o +obj-$(CONFIG_IP_NF_SECURITY) += iptable_security.o # matches obj-$(CONFIG_IP_NF_MATCH_ADDRTYPE) += ipt_addrtype.o diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c new file mode 100644 index 000000000000..2b472ac2263a --- /dev/null +++ b/net/ipv4/netfilter/iptable_security.c @@ -0,0 +1,180 @@ +/* + * "security" table + * + * This is for use by Mandatory Access Control (MAC) security models, + * which need to be able to manage security policy in separate context + * to DAC. + * + * Based on iptable_mangle.c + * + * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling + * Copyright (C) 2000-2004 Netfilter Core Team netfilter.org> + * Copyright (C) 2008 Red Hat, Inc., James Morris redhat.com> + * + * 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 + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("James Morris redhat.com>"); +MODULE_DESCRIPTION("iptables security table, for MAC rules"); + +#define SECURITY_VALID_HOOKS (1 << NF_INET_LOCAL_IN) | \ + (1 << NF_INET_FORWARD) | \ + (1 << NF_INET_LOCAL_OUT) + +static struct +{ + struct ipt_replace repl; + struct ipt_standard entries[3]; + struct ipt_error term; +} initial_table __initdata = { + .repl = { + .name = "security", + .valid_hooks = SECURITY_VALID_HOOKS, + .num_entries = 4, + .size = sizeof(struct ipt_standard) * 3 + sizeof(struct ipt_error), + .hook_entry = { + [NF_INET_LOCAL_IN] = 0, + [NF_INET_FORWARD] = sizeof(struct ipt_standard), + [NF_INET_LOCAL_OUT] = sizeof(struct ipt_standard) * 2, + }, + .underflow = { + [NF_INET_LOCAL_IN] = 0, + [NF_INET_FORWARD] = sizeof(struct ipt_standard), + [NF_INET_LOCAL_OUT] = sizeof(struct ipt_standard) * 2, + }, + }, + .entries = { + IPT_STANDARD_INIT(NF_ACCEPT), /* LOCAL_IN */ + IPT_STANDARD_INIT(NF_ACCEPT), /* FORWARD */ + IPT_STANDARD_INIT(NF_ACCEPT), /* LOCAL_OUT */ + }, + .term = IPT_ERROR_INIT, /* ERROR */ +}; + +static struct xt_table security_table = { + .name = "security", + .valid_hooks = SECURITY_VALID_HOOKS, + .lock = __RW_LOCK_UNLOCKED(security_table.lock), + .me = THIS_MODULE, + .af = AF_INET, +}; + +static unsigned int +ipt_local_in_hook(unsigned int hook, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + return ipt_do_table(skb, hook, in, out, + nf_local_in_net(in, out)->ipv4.iptable_security); +} + +static unsigned int +ipt_forward_hook(unsigned int hook, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + return ipt_do_table(skb, hook, in, out, + nf_forward_net(in, out)->ipv4.iptable_security); +} + +static unsigned int +ipt_local_out_hook(unsigned int hook, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + /* Somebody is playing with raw sockets. */ + if (skb->len < sizeof(struct iphdr) + || ip_hdrlen(skb) < sizeof(struct iphdr)) { + if (net_ratelimit()) + printk(KERN_INFO "iptable_security: ignoring short " + "SOCK_RAW packet.\n"); + return NF_ACCEPT; + } + return ipt_do_table(skb, hook, in, out, + nf_local_out_net(in, out)->ipv4.iptable_security); +} + +static struct nf_hook_ops ipt_ops[] __read_mostly = { + { + .hook = ipt_local_in_hook, + .owner = THIS_MODULE, + .pf = PF_INET, + .hooknum = NF_INET_LOCAL_IN, + .priority = NF_IP_PRI_SECURITY, + }, + { + .hook = ipt_forward_hook, + .owner = THIS_MODULE, + .pf = PF_INET, + .hooknum = NF_INET_FORWARD, + .priority = NF_IP_PRI_SECURITY, + }, + { + .hook = ipt_local_out_hook, + .owner = THIS_MODULE, + .pf = PF_INET, + .hooknum = NF_INET_LOCAL_OUT, + .priority = NF_IP_PRI_SECURITY, + }, +}; + +static int __net_init iptable_security_net_init(struct net *net) +{ + net->ipv4.iptable_security = + ipt_register_table(net, &security_table, &initial_table.repl); + + if (IS_ERR(net->ipv4.iptable_security)) + return PTR_ERR(net->ipv4.iptable_security); + + return 0; +} + +static void __net_exit iptable_security_net_exit(struct net *net) +{ + ipt_unregister_table(net->ipv4.iptable_security); +} + +static struct pernet_operations iptable_security_net_ops = { + .init = iptable_security_net_init, + .exit = iptable_security_net_exit, +}; + +static int __init iptable_security_init(void) +{ + int ret; + + ret = register_pernet_subsys(&iptable_security_net_ops); + if (ret < 0) + return ret; + + ret = nf_register_hooks(ipt_ops, ARRAY_SIZE(ipt_ops)); + if (ret < 0) + goto cleanup_table; + + return ret; + +cleanup_table: + unregister_pernet_subsys(&iptable_security_net_ops); + return ret; +} + +static void __exit iptable_security_fini(void) +{ + nf_unregister_hooks(ipt_ops, ARRAY_SIZE(ipt_ops)); + unregister_pernet_subsys(&iptable_security_net_ops); +} + +module_init(iptable_security_init); +module_exit(iptable_security_fini); diff --git a/net/netfilter/xt_CONNSECMARK.c b/net/netfilter/xt_CONNSECMARK.c index 211189eb2b67..76ca1f2421eb 100644 --- a/net/netfilter/xt_CONNSECMARK.c +++ b/net/netfilter/xt_CONNSECMARK.c @@ -8,7 +8,7 @@ * Copyright (C) 2002,2004 MARA Systems AB * by Henrik Nordstrom * - * (C) 2006 Red Hat, Inc., James Morris + * (C) 2006,2008 Red Hat, Inc., James Morris * * 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 @@ -94,6 +94,12 @@ connsecmark_tg_check(const char *tablename, const void *entry, { const struct xt_connsecmark_target_info *info = targinfo; + if (strcmp(tablename, "mangle") && strcmp(tablename, "security")) { + printk(KERN_INFO PFX "target only valid in the \'mangle\' " + "or \'security\' tables, not \'%s\'.\n", tablename); + return false; + } + switch (info->mode) { case CONNSECMARK_SAVE: case CONNSECMARK_RESTORE: @@ -126,7 +132,6 @@ static struct xt_target connsecmark_tg_reg[] __read_mostly = { .destroy = connsecmark_tg_destroy, .target = connsecmark_tg, .targetsize = sizeof(struct xt_connsecmark_target_info), - .table = "mangle", .me = THIS_MODULE, }, { @@ -136,7 +141,6 @@ static struct xt_target connsecmark_tg_reg[] __read_mostly = { .destroy = connsecmark_tg_destroy, .target = connsecmark_tg, .targetsize = sizeof(struct xt_connsecmark_target_info), - .table = "mangle", .me = THIS_MODULE, }, }; diff --git a/net/netfilter/xt_SECMARK.c b/net/netfilter/xt_SECMARK.c index c0284856ccd4..94f87ee7552b 100644 --- a/net/netfilter/xt_SECMARK.c +++ b/net/netfilter/xt_SECMARK.c @@ -5,7 +5,7 @@ * Based on the nfmark match by: * (C) 1999-2001 Marc Boucher * - * (C) 2006 Red Hat, Inc., James Morris + * (C) 2006,2008 Red Hat, Inc., James Morris * * 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 @@ -89,6 +89,12 @@ secmark_tg_check(const char *tablename, const void *entry, { struct xt_secmark_target_info *info = targinfo; + if (strcmp(tablename, "mangle") && strcmp(tablename, "security")) { + printk(KERN_INFO PFX "target only valid in the \'mangle\' " + "or \'security\' tables, not \'%s\'.\n", tablename); + return false; + } + if (mode && mode != info->mode) { printk(KERN_INFO PFX "mode already set to %hu cannot mix with " "rules for mode %hu\n", mode, info->mode); @@ -127,7 +133,6 @@ static struct xt_target secmark_tg_reg[] __read_mostly = { .destroy = secmark_tg_destroy, .target = secmark_tg, .targetsize = sizeof(struct xt_secmark_target_info), - .table = "mangle", .me = THIS_MODULE, }, { @@ -137,7 +142,6 @@ static struct xt_target secmark_tg_reg[] __read_mostly = { .destroy = secmark_tg_destroy, .target = secmark_tg, .targetsize = sizeof(struct xt_secmark_target_info), - .table = "mangle", .me = THIS_MODULE, }, }; -- cgit v1.2.3 From 17e6e59f0a1d7188d783c15dc3ccebd95a0840cd Mon Sep 17 00:00:00 2001 From: James Morris Date: Mon, 9 Jun 2008 15:58:05 -0700 Subject: netfilter: ip6_tables: add ip6tables security table This is a port of the IPv4 security table for IPv6. Signed-off-by: James Morris Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netfilter_ipv6.h | 1 + include/net/netns/ipv6.h | 1 + net/ipv6/netfilter/Kconfig | 12 +++ net/ipv6/netfilter/Makefile | 1 + net/ipv6/netfilter/ip6table_security.c | 172 +++++++++++++++++++++++++++++++++ 5 files changed, 187 insertions(+) create mode 100644 net/ipv6/netfilter/ip6table_security.c (limited to 'include/linux') diff --git a/include/linux/netfilter_ipv6.h b/include/linux/netfilter_ipv6.h index 3475a65dae9b..fd50988b83ec 100644 --- a/include/linux/netfilter_ipv6.h +++ b/include/linux/netfilter_ipv6.h @@ -64,6 +64,7 @@ enum nf_ip6_hook_priorities { NF_IP6_PRI_MANGLE = -150, NF_IP6_PRI_NAT_DST = -100, NF_IP6_PRI_FILTER = 0, + NF_IP6_PRI_SECURITY = 50, NF_IP6_PRI_NAT_SRC = 100, NF_IP6_PRI_SELINUX_LAST = 225, NF_IP6_PRI_LAST = INT_MAX, diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index ac053be6c256..5bacd838e88b 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -35,6 +35,7 @@ struct netns_ipv6 { struct xt_table *ip6table_filter; struct xt_table *ip6table_mangle; struct xt_table *ip6table_raw; + struct xt_table *ip6table_security; #endif struct rt6_info *ip6_null_entry; struct rt6_statistics *rt6_stats; diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig index 6cae5475737e..689dec899c57 100644 --- a/net/ipv6/netfilter/Kconfig +++ b/net/ipv6/netfilter/Kconfig @@ -208,5 +208,17 @@ config IP6_NF_RAW If you want to compile it as a module, say M here and read . If unsure, say `N'. +# security table for MAC policy +config IP6_NF_SECURITY + tristate "Security table" + depends on IP6_NF_IPTABLES + depends on SECURITY + default m if NETFILTER_ADVANCED=n + help + This option adds a `security' table to iptables, for use + with Mandatory Access Control (MAC) policy. + + If unsure, say N. + endmenu diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile index fbf2c14ed887..3f17c948eefb 100644 --- a/net/ipv6/netfilter/Makefile +++ b/net/ipv6/netfilter/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_IP6_NF_FILTER) += ip6table_filter.o obj-$(CONFIG_IP6_NF_MANGLE) += ip6table_mangle.o obj-$(CONFIG_IP6_NF_QUEUE) += ip6_queue.o obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o +obj-$(CONFIG_IP6_NF_SECURITY) += ip6table_security.o # objects for l3 independent conntrack nf_conntrack_ipv6-objs := nf_conntrack_l3proto_ipv6.o nf_conntrack_proto_icmpv6.o nf_conntrack_reasm.o diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c new file mode 100644 index 000000000000..063a3d9c3c67 --- /dev/null +++ b/net/ipv6/netfilter/ip6table_security.c @@ -0,0 +1,172 @@ +/* + * "security" table for IPv6 + * + * This is for use by Mandatory Access Control (MAC) security models, + * which need to be able to manage security policy in separate context + * to DAC. + * + * Based on iptable_mangle.c + * + * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling + * Copyright (C) 2000-2004 Netfilter Core Team netfilter.org> + * Copyright (C) 2008 Red Hat, Inc., James Morris redhat.com> + * + * 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 + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("James Morris redhat.com>"); +MODULE_DESCRIPTION("ip6tables security table, for MAC rules"); + +#define SECURITY_VALID_HOOKS (1 << NF_INET_LOCAL_IN) | \ + (1 << NF_INET_FORWARD) | \ + (1 << NF_INET_LOCAL_OUT) + +static struct +{ + struct ip6t_replace repl; + struct ip6t_standard entries[3]; + struct ip6t_error term; +} initial_table __initdata = { + .repl = { + .name = "security", + .valid_hooks = SECURITY_VALID_HOOKS, + .num_entries = 4, + .size = sizeof(struct ip6t_standard) * 3 + sizeof(struct ip6t_error), + .hook_entry = { + [NF_INET_LOCAL_IN] = 0, + [NF_INET_FORWARD] = sizeof(struct ip6t_standard), + [NF_INET_LOCAL_OUT] = sizeof(struct ip6t_standard) * 2, + }, + .underflow = { + [NF_INET_LOCAL_IN] = 0, + [NF_INET_FORWARD] = sizeof(struct ip6t_standard), + [NF_INET_LOCAL_OUT] = sizeof(struct ip6t_standard) * 2, + }, + }, + .entries = { + IP6T_STANDARD_INIT(NF_ACCEPT), /* LOCAL_IN */ + IP6T_STANDARD_INIT(NF_ACCEPT), /* FORWARD */ + IP6T_STANDARD_INIT(NF_ACCEPT), /* LOCAL_OUT */ + }, + .term = IP6T_ERROR_INIT, /* ERROR */ +}; + +static struct xt_table security_table = { + .name = "security", + .valid_hooks = SECURITY_VALID_HOOKS, + .lock = __RW_LOCK_UNLOCKED(security_table.lock), + .me = THIS_MODULE, + .af = AF_INET6, +}; + +static unsigned int +ip6t_local_in_hook(unsigned int hook, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + return ip6t_do_table(skb, hook, in, out, + init_net.ipv6.ip6table_security); +} + +static unsigned int +ip6t_forward_hook(unsigned int hook, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + return ip6t_do_table(skb, hook, in, out, + init_net.ipv6.ip6table_security); +} + +static unsigned int +ip6t_local_out_hook(unsigned int hook, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + /* TBD: handle short packets via raw socket */ + return ip6t_do_table(skb, hook, in, out, + init_net.ipv6.ip6table_security); +} + +static struct nf_hook_ops ip6t_ops[] __read_mostly = { + { + .hook = ip6t_local_in_hook, + .owner = THIS_MODULE, + .pf = PF_INET6, + .hooknum = NF_INET_LOCAL_IN, + .priority = NF_IP6_PRI_SECURITY, + }, + { + .hook = ip6t_forward_hook, + .owner = THIS_MODULE, + .pf = PF_INET6, + .hooknum = NF_INET_FORWARD, + .priority = NF_IP6_PRI_SECURITY, + }, + { + .hook = ip6t_local_out_hook, + .owner = THIS_MODULE, + .pf = PF_INET6, + .hooknum = NF_INET_LOCAL_OUT, + .priority = NF_IP6_PRI_SECURITY, + }, +}; + +static int __net_init ip6table_security_net_init(struct net *net) +{ + net->ipv6.ip6table_security = + ip6t_register_table(net, &security_table, &initial_table.repl); + + if (IS_ERR(net->ipv6.ip6table_security)) + return PTR_ERR(net->ipv6.ip6table_security); + + return 0; +} + +static void __net_exit ip6table_security_net_exit(struct net *net) +{ + ip6t_unregister_table(net->ipv6.ip6table_security); +} + +static struct pernet_operations ip6table_security_net_ops = { + .init = ip6table_security_net_init, + .exit = ip6table_security_net_exit, +}; + +static int __init ip6table_security_init(void) +{ + int ret; + + ret = register_pernet_subsys(&ip6table_security_net_ops); + if (ret < 0) + return ret; + + ret = nf_register_hooks(ip6t_ops, ARRAY_SIZE(ip6t_ops)); + if (ret < 0) + goto cleanup_table; + + return ret; + +cleanup_table: + unregister_pernet_subsys(&ip6table_security_net_ops); + return ret; +} + +static void __exit ip6table_security_fini(void) +{ + nf_unregister_hooks(ip6t_ops, ARRAY_SIZE(ip6t_ops)); + unregister_pernet_subsys(&ip6table_security_net_ops); +} + +module_init(ip6table_security_init); +module_exit(ip6table_security_fini); -- cgit v1.2.3 From 19792a0859f96e9fc8ce87d97b269bcb895389e5 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Mon, 5 May 2008 21:25:47 +0300 Subject: PCI: drivers/pci/pci.c: add prototypes This patch adds prototypes for pcibios_disable_device() and pcibios_set_pcie_reset_state() in include/linux/pci.h While I was at it, I also removed the unneeded "extern" from the prototype of pcibios_add_platform_entries(). Signed-off-by: Adrian Bunk Signed-off-by: Jesse Barnes --- include/linux/pci.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/pci.h b/include/linux/pci.h index 509159bcd4e7..aaa9f333fb44 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1059,7 +1059,10 @@ extern int pci_pci_problems; extern unsigned long pci_cardbus_io_size; extern unsigned long pci_cardbus_mem_size; -extern int pcibios_add_platform_entries(struct pci_dev *dev); +int pcibios_add_platform_entries(struct pci_dev *dev); +void pcibios_disable_device(struct pci_dev *dev); +int pcibios_set_pcie_reset_state(struct pci_dev *dev, + enum pcie_reset_state state); #ifdef CONFIG_PCI_MMCONFIG extern void __init pci_mmcfg_early_init(void); -- cgit v1.2.3 From e1a2a51e684bfe9d6165992d4a065439617a3107 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 15 May 2008 21:51:31 +0200 Subject: Suspend/Resume bug in PCI layer wrt quirks Some quirks should be called with interrupt disabled, we can't directly call them in .resume_early. Also the patch introduces pci_fixup_resume_early and pci_fixup_suspend, which matches current device core callbacks (.suspend/.resume_early). TBD: Somebody knows why we need quirk resume should double check if a quirk should be called in resume or resume_early. I changed some per my understanding, but can't make sure I fixed all. Signed-off-by: Shaohua Li Signed-off-by: Jesse Barnes --- drivers/pci/pci-driver.c | 6 +- drivers/pci/quirks.c | 118 ++++++++++++++++++++++++++------------ include/asm-generic/vmlinux.lds.h | 6 ++ include/linux/pci.h | 10 +++- 4 files changed, 101 insertions(+), 39 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 72cf61ed8f96..677fd9d6db12 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -292,6 +292,9 @@ static int pci_device_suspend(struct device * dev, pm_message_t state) if (pci_dev->current_state == PCI_D0) pci_dev->current_state = PCI_UNKNOWN; } + + pci_fixup_device(pci_fixup_suspend, pci_dev); + return i; } @@ -337,6 +340,7 @@ static int pci_device_resume(struct device * dev) error = drv->resume(pci_dev); else error = pci_default_resume(pci_dev); + pci_fixup_device(pci_fixup_resume, pci_dev); return error; } @@ -346,7 +350,7 @@ static int pci_device_resume_early(struct device * dev) struct pci_dev * pci_dev = to_pci_dev(dev); struct pci_driver * drv = pci_dev->driver; - pci_fixup_device(pci_fixup_resume, pci_dev); + pci_fixup_device(pci_fixup_resume_early, pci_dev); if (drv && drv->resume_early) error = drv->resume_early(pci_dev); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index dabb563f51d9..44aabb1519a9 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -556,7 +556,7 @@ static void quirk_via_ioapic(struct pci_dev *dev) pci_write_config_byte (dev, 0x58, tmp); } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, quirk_via_ioapic); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, quirk_via_ioapic); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, quirk_via_ioapic); /* * VIA 8237: Some BIOSs don't set the 'Bypass APIC De-Assert Message' Bit. @@ -576,7 +576,7 @@ static void quirk_via_vt8237_bypass_apic_deassert(struct pci_dev *dev) } } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, quirk_via_vt8237_bypass_apic_deassert); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, quirk_via_vt8237_bypass_apic_deassert); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, quirk_via_vt8237_bypass_apic_deassert); /* * The AMD io apic can hang the box when an apic irq is masked. @@ -622,7 +622,7 @@ static void quirk_amd_8131_ioapic(struct pci_dev *dev) } } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_ioapic); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_ioapic); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_ioapic); #endif /* CONFIG_X86_IO_APIC */ /* @@ -774,7 +774,7 @@ static void quirk_cardbus_legacy(struct pci_dev *dev) pci_write_config_dword(dev, PCI_CB_LEGACY_MODE_BASE, 0); } DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, quirk_cardbus_legacy); -DECLARE_PCI_FIXUP_RESUME(PCI_ANY_ID, PCI_ANY_ID, quirk_cardbus_legacy); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_ANY_ID, PCI_ANY_ID, quirk_cardbus_legacy); /* * Following the PCI ordering rules is optional on the AMD762. I'm not @@ -797,7 +797,7 @@ static void quirk_amd_ordering(struct pci_dev *dev) } } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C, quirk_amd_ordering); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C, quirk_amd_ordering); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C, quirk_amd_ordering); /* * DreamWorks provided workaround for Dunord I-3000 problem @@ -865,7 +865,7 @@ static void quirk_disable_pxb(struct pci_dev *pdev) } } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454NX, quirk_disable_pxb); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454NX, quirk_disable_pxb); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454NX, quirk_disable_pxb); static void __devinit quirk_amd_ide_mode(struct pci_dev *pdev) { @@ -885,9 +885,9 @@ static void __devinit quirk_amd_ide_mode(struct pci_dev *pdev) } } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_SATA, quirk_amd_ide_mode); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_SATA, quirk_amd_ide_mode); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_SATA, quirk_amd_ide_mode); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP700_SATA, quirk_amd_ide_mode); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP700_SATA, quirk_amd_ide_mode); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP700_SATA, quirk_amd_ide_mode); /* * Serverworks CSB5 IDE does not fully support native mode @@ -1093,31 +1093,61 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, asu DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, asus_hides_smbus_lpc); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12, asus_hides_smbus_lpc); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, asus_hides_smbus_lpc); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, asus_hides_smbus_lpc); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, asus_hides_smbus_lpc); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, asus_hides_smbus_lpc); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, asus_hides_smbus_lpc); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, asus_hides_smbus_lpc); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12, asus_hides_smbus_lpc); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, asus_hides_smbus_lpc); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, asus_hides_smbus_lpc); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, asus_hides_smbus_lpc); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, asus_hides_smbus_lpc); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, asus_hides_smbus_lpc); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, asus_hides_smbus_lpc); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12, asus_hides_smbus_lpc); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, asus_hides_smbus_lpc); -static void asus_hides_smbus_lpc_ich6(struct pci_dev *dev) +/* It appears we just have one such device. If not, we have a warning */ +static void __iomem *asus_rcba_base; +static void asus_hides_smbus_lpc_ich6_suspend(struct pci_dev *dev) { - u32 val, rcba; - void __iomem *base; + u32 rcba; if (likely(!asus_hides_smbus)) return; + WARN_ON(asus_rcba_base); + pci_read_config_dword(dev, 0xF0, &rcba); - base = ioremap_nocache(rcba & 0xFFFFC000, 0x4000); /* use bits 31:14, 16 kB aligned */ - if (base == NULL) return; - val=readl(base + 0x3418); /* read the Function Disable register, dword mode only */ - writel(val & 0xFFFFFFF7, base + 0x3418); /* enable the SMBus device */ - iounmap(base); + /* use bits 31:14, 16 kB aligned */ + asus_rcba_base = ioremap_nocache(rcba & 0xFFFFC000, 0x4000); + if (asus_rcba_base == NULL) + return; +} + +static void asus_hides_smbus_lpc_ich6_resume_early(struct pci_dev *dev) +{ + u32 val; + + if (likely(!asus_hides_smbus || !asus_rcba_base)) + return; + /* read the Function Disable register, dword mode only */ + val = readl(asus_rcba_base + 0x3418); + writel(val & 0xFFFFFFF7, asus_rcba_base + 0x3418); /* enable the SMBus device */ +} + +static void asus_hides_smbus_lpc_ich6_resume(struct pci_dev *dev) +{ + if (likely(!asus_hides_smbus || !asus_rcba_base)) + return; + iounmap(asus_rcba_base); + asus_rcba_base = NULL; dev_info(&dev->dev, "Enabled ICH6/i801 SMBus device\n"); } + +static void asus_hides_smbus_lpc_ich6(struct pci_dev *dev) +{ + asus_hides_smbus_lpc_ich6_suspend(dev); + asus_hides_smbus_lpc_ich6_resume_early(dev); + asus_hides_smbus_lpc_ich6_resume(dev); +} DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, asus_hides_smbus_lpc_ich6); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, asus_hides_smbus_lpc_ich6); +DECLARE_PCI_FIXUP_SUSPEND(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, asus_hides_smbus_lpc_ich6_suspend); +DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, asus_hides_smbus_lpc_ich6_resume); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, asus_hides_smbus_lpc_ich6_resume_early); /* * SiS 96x south bridge: BIOS typically hides SMBus device... @@ -1135,10 +1165,10 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_961, quirk_sis_96x_ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_962, quirk_sis_96x_smbus); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_963, quirk_sis_96x_smbus); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC, quirk_sis_96x_smbus); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_961, quirk_sis_96x_smbus); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_962, quirk_sis_96x_smbus); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_963, quirk_sis_96x_smbus); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC, quirk_sis_96x_smbus); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_961, quirk_sis_96x_smbus); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_962, quirk_sis_96x_smbus); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_963, quirk_sis_96x_smbus); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC, quirk_sis_96x_smbus); /* * ... This is further complicated by the fact that some SiS96x south @@ -1172,7 +1202,7 @@ static void quirk_sis_503(struct pci_dev *dev) quirk_sis_96x_smbus(dev); } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, quirk_sis_503); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, quirk_sis_503); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, quirk_sis_503); /* @@ -1205,7 +1235,7 @@ static void asus_hides_ac97_lpc(struct pci_dev *dev) } } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, asus_hides_ac97_lpc); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, asus_hides_ac97_lpc); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, asus_hides_ac97_lpc); #if defined(CONFIG_ATA) || defined(CONFIG_ATA_MODULE) @@ -1270,12 +1300,12 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB363, qui DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB365, quirk_jmicron_ata); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB366, quirk_jmicron_ata); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB368, quirk_jmicron_ata); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB360, quirk_jmicron_ata); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB361, quirk_jmicron_ata); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB363, quirk_jmicron_ata); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB365, quirk_jmicron_ata); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB366, quirk_jmicron_ata); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB368, quirk_jmicron_ata); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB360, quirk_jmicron_ata); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB361, quirk_jmicron_ata); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB363, quirk_jmicron_ata); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB365, quirk_jmicron_ata); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB366, quirk_jmicron_ata); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB368, quirk_jmicron_ata); #endif @@ -1521,6 +1551,10 @@ extern struct pci_fixup __start_pci_fixups_enable[]; extern struct pci_fixup __end_pci_fixups_enable[]; extern struct pci_fixup __start_pci_fixups_resume[]; extern struct pci_fixup __end_pci_fixups_resume[]; +extern struct pci_fixup __start_pci_fixups_resume_early[]; +extern struct pci_fixup __end_pci_fixups_resume_early[]; +extern struct pci_fixup __start_pci_fixups_suspend[]; +extern struct pci_fixup __end_pci_fixups_suspend[]; void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev) @@ -1553,6 +1587,16 @@ void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev) end = __end_pci_fixups_resume; break; + case pci_fixup_resume_early: + start = __start_pci_fixups_resume_early; + end = __end_pci_fixups_resume_early; + break; + + case pci_fixup_suspend: + start = __start_pci_fixups_suspend; + end = __end_pci_fixups_suspend; + break; + default: /* stupid compiler warning, you would think with an enum... */ return; @@ -1629,7 +1673,7 @@ static void quirk_nvidia_ck804_pcie_aer_ext_cap(struct pci_dev *dev) } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE, quirk_nvidia_ck804_pcie_aer_ext_cap); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE, +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE, quirk_nvidia_ck804_pcie_aer_ext_cap); static void __devinit quirk_via_cx700_pci_parking_caching(struct pci_dev *dev) diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index f054778e916c..cf108a3c7f59 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -84,6 +84,12 @@ VMLINUX_SYMBOL(__start_pci_fixups_resume) = .; \ *(.pci_fixup_resume) \ VMLINUX_SYMBOL(__end_pci_fixups_resume) = .; \ + VMLINUX_SYMBOL(__start_pci_fixups_resume_early) = .; \ + *(.pci_fixup_resume_early) \ + VMLINUX_SYMBOL(__end_pci_fixups_resume_early) = .; \ + VMLINUX_SYMBOL(__start_pci_fixups_suspend) = .; \ + *(.pci_fixup_suspend) \ + VMLINUX_SYMBOL(__end_pci_fixups_suspend) = .; \ } \ \ /* RapidIO route ops */ \ diff --git a/include/linux/pci.h b/include/linux/pci.h index aaa9f333fb44..700704ef70f3 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1013,7 +1013,9 @@ enum pci_fixup_pass { pci_fixup_header, /* After reading configuration header */ pci_fixup_final, /* Final phase of device fixups */ pci_fixup_enable, /* pci_enable_device() time */ - pci_fixup_resume, /* pci_enable_device() time */ + pci_fixup_resume, /* pci_device_resume() */ + pci_fixup_suspend, /* pci_device_suspend */ + pci_fixup_resume_early, /* pci_device_resume_early() */ }; /* Anonymous variables would be nice... */ @@ -1035,6 +1037,12 @@ enum pci_fixup_pass { #define DECLARE_PCI_FIXUP_RESUME(vendor, device, hook) \ DECLARE_PCI_FIXUP_SECTION(.pci_fixup_resume, \ resume##vendor##device##hook, vendor, device, hook) +#define DECLARE_PCI_FIXUP_RESUME_EARLY(vendor, device, hook) \ + DECLARE_PCI_FIXUP_SECTION(.pci_fixup_resume_early, \ + resume_early##vendor##device##hook, vendor, device, hook) +#define DECLARE_PCI_FIXUP_SUSPEND(vendor, device, hook) \ + DECLARE_PCI_FIXUP_SECTION(.pci_fixup_suspend, \ + suspend##vendor##device##hook, vendor, device, hook) void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev); -- cgit v1.2.3 From 1eede070a59e1cc73da51e1aaa00d9ab86572cfc Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 20 May 2008 23:00:01 +0200 Subject: Introduce new top level suspend and hibernation callbacks Introduce 'struct pm_ops' and 'struct pm_ext_ops' ('ext' meaning 'extended') representing suspend and hibernation operations for bus types, device classes, device types and device drivers. Modify the PM core to use 'struct pm_ops' and 'struct pm_ext_ops' objects, if defined, instead of the ->suspend(), ->resume(), ->suspend_late(), and ->resume_early() callbacks (the old callbacks will be considered as legacy and gradually phased out). The main purpose of doing this is to separate suspend (aka S2RAM and standby) callbacks from hibernation callbacks in such a way that the new callbacks won't take arguments and the semantics of each of them will be clearly specified. This has been requested for multiple times by many people, including Linus himself, and the reason is that within the current scheme if ->resume() is called, for example, it's difficult to say why it's been called (ie. is it a resume from RAM or from hibernation or a suspend/hibernation failure etc.?). The second purpose is to make the suspend/hibernation callbacks more flexible so that device drivers can handle more than they can within the current scheme. For example, some drivers may need to prevent new children of the device from being registered before their ->suspend() callbacks are executed or they may want to carry out some operations requiring the availability of some other devices, not directly bound via the parent-child relationship, in order to prepare for the execution of ->suspend(), etc. Ultimately, we'd like to stop using the freezing of tasks for suspend and therefore the drivers' suspend/hibernation code will have to take care of the handling of the user space during suspend/hibernation. That, in turn, would be difficult within the current scheme, without the new ->prepare() and ->complete() callbacks. Signed-off-by: Rafael J. Wysocki Acked-by: Pavel Machek Signed-off-by: Jesse Barnes --- arch/x86/kernel/apm_32.c | 8 +- drivers/base/power/main.c | 675 +++++++++++++++++++++++++++++++++++---------- drivers/base/power/power.h | 2 +- drivers/base/power/trace.c | 4 +- include/linux/device.h | 9 + include/linux/pm.h | 314 +++++++++++++++++++-- kernel/power/disk.c | 22 +- kernel/power/main.c | 6 +- 8 files changed, 845 insertions(+), 195 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/apm_32.c b/arch/x86/kernel/apm_32.c index bf9290e29013..c1735f61a2c0 100644 --- a/arch/x86/kernel/apm_32.c +++ b/arch/x86/kernel/apm_32.c @@ -1211,9 +1211,9 @@ static int suspend(int vetoable) if (err != APM_SUCCESS) apm_error("suspend", err); err = (err == APM_SUCCESS) ? 0 : -EIO; - device_power_up(); + device_power_up(PMSG_RESUME); local_irq_enable(); - device_resume(); + device_resume(PMSG_RESUME); queue_event(APM_NORMAL_RESUME, NULL); spin_lock(&user_list_lock); for (as = user_list; as != NULL; as = as->next) { @@ -1238,7 +1238,7 @@ static void standby(void) apm_error("standby", err); local_irq_disable(); - device_power_up(); + device_power_up(PMSG_RESUME); local_irq_enable(); } @@ -1324,7 +1324,7 @@ static void check_events(void) ignore_bounce = 1; if ((event != APM_NORMAL_RESUME) || (ignore_normal_resume == 0)) { - device_resume(); + device_resume(PMSG_RESUME); queue_event(event, NULL); } ignore_normal_resume = 0; diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 45cc3d9eacb8..d571204aaff7 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -12,11 +12,9 @@ * and add it to the list of power-controlled devices. sysfs entries for * controlling device power management will also be added. * - * A different set of lists than the global subsystem list are used to - * keep track of power info because we use different lists to hold - * devices based on what stage of the power management process they - * are in. The power domain dependencies may also differ from the - * ancestral dependencies that the subsystem list maintains. + * A separate list is used for keeping track of power info, because the power + * domain dependencies may differ from the ancestral dependencies that the + * subsystem list maintains. */ #include @@ -30,31 +28,40 @@ #include "power.h" /* - * The entries in the dpm_active list are in a depth first order, simply + * The entries in the dpm_list list are in a depth first order, simply * because children are guaranteed to be discovered after parents, and * are inserted at the back of the list on discovery. * - * All the other lists are kept in the same order, for consistency. - * However the lists aren't always traversed in the same order. - * Semaphores must be acquired from the top (i.e., front) down - * and released in the opposite order. Devices must be suspended - * from the bottom (i.e., end) up and resumed in the opposite order. - * That way no parent will be suspended while it still has an active - * child. - * * Since device_pm_add() may be called with a device semaphore held, * we must never try to acquire a device semaphore while holding * dpm_list_mutex. */ -LIST_HEAD(dpm_active); -static LIST_HEAD(dpm_off); -static LIST_HEAD(dpm_off_irq); +LIST_HEAD(dpm_list); static DEFINE_MUTEX(dpm_list_mtx); -/* 'true' if all devices have been suspended, protected by dpm_list_mtx */ -static bool all_sleeping; +/* + * Set once the preparation of devices for a PM transition has started, reset + * before starting to resume devices. Protected by dpm_list_mtx. + */ +static bool transition_started; + +/** + * device_pm_lock - lock the list of active devices used by the PM core + */ +void device_pm_lock(void) +{ + mutex_lock(&dpm_list_mtx); +} + +/** + * device_pm_unlock - unlock the list of active devices used by the PM core + */ +void device_pm_unlock(void) +{ + mutex_unlock(&dpm_list_mtx); +} /** * device_pm_add - add a device to the list of active devices @@ -68,17 +75,25 @@ int device_pm_add(struct device *dev) dev->bus ? dev->bus->name : "No Bus", kobject_name(&dev->kobj)); mutex_lock(&dpm_list_mtx); - if ((dev->parent && dev->parent->power.sleeping) || all_sleeping) { - if (dev->parent->power.sleeping) - dev_warn(dev, "parent %s is sleeping\n", + if (dev->parent) { + if (dev->parent->power.status >= DPM_SUSPENDING) { + dev_warn(dev, "parent %s is sleeping, will not add\n", dev->parent->bus_id); - else - dev_warn(dev, "all devices are sleeping\n"); + WARN_ON(true); + } + } else if (transition_started) { + /* + * We refuse to register parentless devices while a PM + * transition is in progress in order to avoid leaving them + * unhandled down the road + */ WARN_ON(true); } error = dpm_sysfs_add(dev); - if (!error) - list_add_tail(&dev->power.entry, &dpm_active); + if (!error) { + dev->power.status = DPM_ON; + list_add_tail(&dev->power.entry, &dpm_list); + } mutex_unlock(&dpm_list_mtx); return error; } @@ -100,73 +115,243 @@ void device_pm_remove(struct device *dev) mutex_unlock(&dpm_list_mtx); } +/** + * pm_op - execute the PM operation appropiate for given PM event + * @dev: Device. + * @ops: PM operations to choose from. + * @state: PM transition of the system being carried out. + */ +static int pm_op(struct device *dev, struct pm_ops *ops, pm_message_t state) +{ + int error = 0; + + switch (state.event) { +#ifdef CONFIG_SUSPEND + case PM_EVENT_SUSPEND: + if (ops->suspend) { + error = ops->suspend(dev); + suspend_report_result(ops->suspend, error); + } + break; + case PM_EVENT_RESUME: + if (ops->resume) { + error = ops->resume(dev); + suspend_report_result(ops->resume, error); + } + break; +#endif /* CONFIG_SUSPEND */ +#ifdef CONFIG_HIBERNATION + case PM_EVENT_FREEZE: + case PM_EVENT_QUIESCE: + if (ops->freeze) { + error = ops->freeze(dev); + suspend_report_result(ops->freeze, error); + } + break; + case PM_EVENT_HIBERNATE: + if (ops->poweroff) { + error = ops->poweroff(dev); + suspend_report_result(ops->poweroff, error); + } + break; + case PM_EVENT_THAW: + case PM_EVENT_RECOVER: + if (ops->thaw) { + error = ops->thaw(dev); + suspend_report_result(ops->thaw, error); + } + break; + case PM_EVENT_RESTORE: + if (ops->restore) { + error = ops->restore(dev); + suspend_report_result(ops->restore, error); + } + break; +#endif /* CONFIG_HIBERNATION */ + default: + error = -EINVAL; + } + return error; +} + +/** + * pm_noirq_op - execute the PM operation appropiate for given PM event + * @dev: Device. + * @ops: PM operations to choose from. + * @state: PM transition of the system being carried out. + * + * The operation is executed with interrupts disabled by the only remaining + * functional CPU in the system. + */ +static int pm_noirq_op(struct device *dev, struct pm_ext_ops *ops, + pm_message_t state) +{ + int error = 0; + + switch (state.event) { +#ifdef CONFIG_SUSPEND + case PM_EVENT_SUSPEND: + if (ops->suspend_noirq) { + error = ops->suspend_noirq(dev); + suspend_report_result(ops->suspend_noirq, error); + } + break; + case PM_EVENT_RESUME: + if (ops->resume_noirq) { + error = ops->resume_noirq(dev); + suspend_report_result(ops->resume_noirq, error); + } + break; +#endif /* CONFIG_SUSPEND */ +#ifdef CONFIG_HIBERNATION + case PM_EVENT_FREEZE: + case PM_EVENT_QUIESCE: + if (ops->freeze_noirq) { + error = ops->freeze_noirq(dev); + suspend_report_result(ops->freeze_noirq, error); + } + break; + case PM_EVENT_HIBERNATE: + if (ops->poweroff_noirq) { + error = ops->poweroff_noirq(dev); + suspend_report_result(ops->poweroff_noirq, error); + } + break; + case PM_EVENT_THAW: + case PM_EVENT_RECOVER: + if (ops->thaw_noirq) { + error = ops->thaw_noirq(dev); + suspend_report_result(ops->thaw_noirq, error); + } + break; + case PM_EVENT_RESTORE: + if (ops->restore_noirq) { + error = ops->restore_noirq(dev); + suspend_report_result(ops->restore_noirq, error); + } + break; +#endif /* CONFIG_HIBERNATION */ + default: + error = -EINVAL; + } + return error; +} + +static char *pm_verb(int event) +{ + switch (event) { + case PM_EVENT_SUSPEND: + return "suspend"; + case PM_EVENT_RESUME: + return "resume"; + case PM_EVENT_FREEZE: + return "freeze"; + case PM_EVENT_QUIESCE: + return "quiesce"; + case PM_EVENT_HIBERNATE: + return "hibernate"; + case PM_EVENT_THAW: + return "thaw"; + case PM_EVENT_RESTORE: + return "restore"; + case PM_EVENT_RECOVER: + return "recover"; + default: + return "(unknown PM event)"; + } +} + +static void pm_dev_dbg(struct device *dev, pm_message_t state, char *info) +{ + dev_dbg(dev, "%s%s%s\n", info, pm_verb(state.event), + ((state.event & PM_EVENT_SLEEP) && device_may_wakeup(dev)) ? + ", may wakeup" : ""); +} + +static void pm_dev_err(struct device *dev, pm_message_t state, char *info, + int error) +{ + printk(KERN_ERR "PM: Device %s failed to %s%s: error %d\n", + kobject_name(&dev->kobj), pm_verb(state.event), info, error); +} + /*------------------------- Resume routines -------------------------*/ /** - * resume_device_early - Power on one device (early resume). + * resume_device_noirq - Power on one device (early resume). * @dev: Device. + * @state: PM transition of the system being carried out. * * Must be called with interrupts disabled. */ -static int resume_device_early(struct device *dev) +static int resume_device_noirq(struct device *dev, pm_message_t state) { int error = 0; TRACE_DEVICE(dev); TRACE_RESUME(0); - if (dev->bus && dev->bus->resume_early) { - dev_dbg(dev, "EARLY resume\n"); + if (!dev->bus) + goto End; + + if (dev->bus->pm) { + pm_dev_dbg(dev, state, "EARLY "); + error = pm_noirq_op(dev, dev->bus->pm, state); + } else if (dev->bus->resume_early) { + pm_dev_dbg(dev, state, "legacy EARLY "); error = dev->bus->resume_early(dev); } - + End: TRACE_RESUME(error); return error; } /** * dpm_power_up - Power on all regular (non-sysdev) devices. + * @state: PM transition of the system being carried out. * - * Walk the dpm_off_irq list and power each device up. This - * is used for devices that required they be powered down with - * interrupts disabled. As devices are powered on, they are moved - * to the dpm_off list. + * Execute the appropriate "noirq resume" callback for all devices marked + * as DPM_OFF_IRQ. * * Must be called with interrupts disabled and only one CPU running. */ -static void dpm_power_up(void) +static void dpm_power_up(pm_message_t state) { + struct device *dev; - while (!list_empty(&dpm_off_irq)) { - struct list_head *entry = dpm_off_irq.next; - struct device *dev = to_device(entry); + list_for_each_entry(dev, &dpm_list, power.entry) + if (dev->power.status > DPM_OFF) { + int error; - list_move_tail(entry, &dpm_off); - resume_device_early(dev); - } + dev->power.status = DPM_OFF; + error = resume_device_noirq(dev, state); + if (error) + pm_dev_err(dev, state, " early", error); + } } /** * device_power_up - Turn on all devices that need special attention. + * @state: PM transition of the system being carried out. * * Power on system devices, then devices that required we shut them down * with interrupts disabled. * * Must be called with interrupts disabled. */ -void device_power_up(void) +void device_power_up(pm_message_t state) { sysdev_resume(); - dpm_power_up(); + dpm_power_up(state); } EXPORT_SYMBOL_GPL(device_power_up); /** * resume_device - Restore state for one device. * @dev: Device. - * + * @state: PM transition of the system being carried out. */ -static int resume_device(struct device *dev) +static int resume_device(struct device *dev, pm_message_t state) { int error = 0; @@ -175,21 +360,40 @@ static int resume_device(struct device *dev) down(&dev->sem); - if (dev->bus && dev->bus->resume) { - dev_dbg(dev,"resuming\n"); - error = dev->bus->resume(dev); + if (dev->bus) { + if (dev->bus->pm) { + pm_dev_dbg(dev, state, ""); + error = pm_op(dev, &dev->bus->pm->base, state); + } else if (dev->bus->resume) { + pm_dev_dbg(dev, state, "legacy "); + error = dev->bus->resume(dev); + } + if (error) + goto End; } - if (!error && dev->type && dev->type->resume) { - dev_dbg(dev,"resuming\n"); - error = dev->type->resume(dev); + if (dev->type) { + if (dev->type->pm) { + pm_dev_dbg(dev, state, "type "); + error = pm_op(dev, dev->type->pm, state); + } else if (dev->type->resume) { + pm_dev_dbg(dev, state, "legacy type "); + error = dev->type->resume(dev); + } + if (error) + goto End; } - if (!error && dev->class && dev->class->resume) { - dev_dbg(dev,"class resume\n"); - error = dev->class->resume(dev); + if (dev->class) { + if (dev->class->pm) { + pm_dev_dbg(dev, state, "class "); + error = pm_op(dev, dev->class->pm, state); + } else if (dev->class->resume) { + pm_dev_dbg(dev, state, "legacy class "); + error = dev->class->resume(dev); + } } - + End: up(&dev->sem); TRACE_RESUME(error); @@ -198,78 +402,161 @@ static int resume_device(struct device *dev) /** * dpm_resume - Resume every device. + * @state: PM transition of the system being carried out. * - * Resume the devices that have either not gone through - * the late suspend, or that did go through it but also - * went through the early resume. + * Execute the appropriate "resume" callback for all devices the status of + * which indicates that they are inactive. + */ +static void dpm_resume(pm_message_t state) +{ + struct list_head list; + + INIT_LIST_HEAD(&list); + mutex_lock(&dpm_list_mtx); + transition_started = false; + while (!list_empty(&dpm_list)) { + struct device *dev = to_device(dpm_list.next); + + get_device(dev); + if (dev->power.status >= DPM_OFF) { + int error; + + dev->power.status = DPM_RESUMING; + mutex_unlock(&dpm_list_mtx); + + error = resume_device(dev, state); + + mutex_lock(&dpm_list_mtx); + if (error) + pm_dev_err(dev, state, "", error); + } else if (dev->power.status == DPM_SUSPENDING) { + /* Allow new children of the device to be registered */ + dev->power.status = DPM_RESUMING; + } + if (!list_empty(&dev->power.entry)) + list_move_tail(&dev->power.entry, &list); + put_device(dev); + } + list_splice(&list, &dpm_list); + mutex_unlock(&dpm_list_mtx); +} + +/** + * complete_device - Complete a PM transition for given device + * @dev: Device. + * @state: PM transition of the system being carried out. + */ +static void complete_device(struct device *dev, pm_message_t state) +{ + down(&dev->sem); + + if (dev->class && dev->class->pm && dev->class->pm->complete) { + pm_dev_dbg(dev, state, "completing class "); + dev->class->pm->complete(dev); + } + + if (dev->type && dev->type->pm && dev->type->pm->complete) { + pm_dev_dbg(dev, state, "completing type "); + dev->type->pm->complete(dev); + } + + if (dev->bus && dev->bus->pm && dev->bus->pm->base.complete) { + pm_dev_dbg(dev, state, "completing "); + dev->bus->pm->base.complete(dev); + } + + up(&dev->sem); +} + +/** + * dpm_complete - Complete a PM transition for all devices. + * @state: PM transition of the system being carried out. * - * Take devices from the dpm_off_list, resume them, - * and put them on the dpm_locked list. + * Execute the ->complete() callbacks for all devices that are not marked + * as DPM_ON. */ -static void dpm_resume(void) +static void dpm_complete(pm_message_t state) { + struct list_head list; + + INIT_LIST_HEAD(&list); mutex_lock(&dpm_list_mtx); - all_sleeping = false; - while(!list_empty(&dpm_off)) { - struct list_head *entry = dpm_off.next; - struct device *dev = to_device(entry); + while (!list_empty(&dpm_list)) { + struct device *dev = to_device(dpm_list.prev); - list_move_tail(entry, &dpm_active); - dev->power.sleeping = false; - mutex_unlock(&dpm_list_mtx); - resume_device(dev); - mutex_lock(&dpm_list_mtx); + get_device(dev); + if (dev->power.status > DPM_ON) { + dev->power.status = DPM_ON; + mutex_unlock(&dpm_list_mtx); + + complete_device(dev, state); + + mutex_lock(&dpm_list_mtx); + } + if (!list_empty(&dev->power.entry)) + list_move(&dev->power.entry, &list); + put_device(dev); } + list_splice(&list, &dpm_list); mutex_unlock(&dpm_list_mtx); } /** * device_resume - Restore state of each device in system. + * @state: PM transition of the system being carried out. * * Resume all the devices, unlock them all, and allow new * devices to be registered once again. */ -void device_resume(void) +void device_resume(pm_message_t state) { might_sleep(); - dpm_resume(); + dpm_resume(state); + dpm_complete(state); } EXPORT_SYMBOL_GPL(device_resume); /*------------------------- Suspend routines -------------------------*/ -static inline char *suspend_verb(u32 event) +/** + * resume_event - return a PM message representing the resume event + * corresponding to given sleep state. + * @sleep_state: PM message representing a sleep state. + */ +static pm_message_t resume_event(pm_message_t sleep_state) { - switch (event) { - case PM_EVENT_SUSPEND: return "suspend"; - case PM_EVENT_FREEZE: return "freeze"; - case PM_EVENT_PRETHAW: return "prethaw"; - default: return "(unknown suspend event)"; + switch (sleep_state.event) { + case PM_EVENT_SUSPEND: + return PMSG_RESUME; + case PM_EVENT_FREEZE: + case PM_EVENT_QUIESCE: + return PMSG_RECOVER; + case PM_EVENT_HIBERNATE: + return PMSG_RESTORE; } -} - -static void -suspend_device_dbg(struct device *dev, pm_message_t state, char *info) -{ - dev_dbg(dev, "%s%s%s\n", info, suspend_verb(state.event), - ((state.event == PM_EVENT_SUSPEND) && device_may_wakeup(dev)) ? - ", may wakeup" : ""); + return PMSG_ON; } /** - * suspend_device_late - Shut down one device (late suspend). + * suspend_device_noirq - Shut down one device (late suspend). * @dev: Device. - * @state: Power state device is entering. + * @state: PM transition of the system being carried out. * * This is called with interrupts off and only a single CPU running. */ -static int suspend_device_late(struct device *dev, pm_message_t state) +static int suspend_device_noirq(struct device *dev, pm_message_t state) { int error = 0; - if (dev->bus && dev->bus->suspend_late) { - suspend_device_dbg(dev, state, "LATE "); + if (!dev->bus) + return 0; + + if (dev->bus->pm) { + pm_dev_dbg(dev, state, "LATE "); + error = pm_noirq_op(dev, dev->bus->pm, state); + } else if (dev->bus->suspend_late) { + pm_dev_dbg(dev, state, "legacy LATE "); error = dev->bus->suspend_late(dev, state); suspend_report_result(dev->bus->suspend_late, error); } @@ -278,37 +565,30 @@ static int suspend_device_late(struct device *dev, pm_message_t state) /** * device_power_down - Shut down special devices. - * @state: Power state to enter. + * @state: PM transition of the system being carried out. * - * Power down devices that require interrupts to be disabled - * and move them from the dpm_off list to the dpm_off_irq list. + * Power down devices that require interrupts to be disabled. * Then power down system devices. * * Must be called with interrupts disabled and only one CPU running. */ int device_power_down(pm_message_t state) { + struct device *dev; int error = 0; - while (!list_empty(&dpm_off)) { - struct list_head *entry = dpm_off.prev; - struct device *dev = to_device(entry); - - error = suspend_device_late(dev, state); + list_for_each_entry_reverse(dev, &dpm_list, power.entry) { + error = suspend_device_noirq(dev, state); if (error) { - printk(KERN_ERR "Could not power down device %s: " - "error %d\n", - kobject_name(&dev->kobj), error); + pm_dev_err(dev, state, " late", error); break; } - if (!list_empty(&dev->power.entry)) - list_move(&dev->power.entry, &dpm_off_irq); + dev->power.status = DPM_OFF_IRQ; } - if (!error) error = sysdev_suspend(state); if (error) - dpm_power_up(); + dpm_power_up(resume_event(state)); return error; } EXPORT_SYMBOL_GPL(device_power_down); @@ -316,7 +596,7 @@ EXPORT_SYMBOL_GPL(device_power_down); /** * suspend_device - Save state of one device. * @dev: Device. - * @state: Power state device is entering. + * @state: PM transition of the system being carried out. */ static int suspend_device(struct device *dev, pm_message_t state) { @@ -324,24 +604,43 @@ static int suspend_device(struct device *dev, pm_message_t state) down(&dev->sem); - if (dev->class && dev->class->suspend) { - suspend_device_dbg(dev, state, "class "); - error = dev->class->suspend(dev, state); - suspend_report_result(dev->class->suspend, error); + if (dev->class) { + if (dev->class->pm) { + pm_dev_dbg(dev, state, "class "); + error = pm_op(dev, dev->class->pm, state); + } else if (dev->class->suspend) { + pm_dev_dbg(dev, state, "legacy class "); + error = dev->class->suspend(dev, state); + suspend_report_result(dev->class->suspend, error); + } + if (error) + goto End; } - if (!error && dev->type && dev->type->suspend) { - suspend_device_dbg(dev, state, "type "); - error = dev->type->suspend(dev, state); - suspend_report_result(dev->type->suspend, error); + if (dev->type) { + if (dev->type->pm) { + pm_dev_dbg(dev, state, "type "); + error = pm_op(dev, dev->type->pm, state); + } else if (dev->type->suspend) { + pm_dev_dbg(dev, state, "legacy type "); + error = dev->type->suspend(dev, state); + suspend_report_result(dev->type->suspend, error); + } + if (error) + goto End; } - if (!error && dev->bus && dev->bus->suspend) { - suspend_device_dbg(dev, state, ""); - error = dev->bus->suspend(dev, state); - suspend_report_result(dev->bus->suspend, error); + if (dev->bus) { + if (dev->bus->pm) { + pm_dev_dbg(dev, state, ""); + error = pm_op(dev, &dev->bus->pm->base, state); + } else if (dev->bus->suspend) { + pm_dev_dbg(dev, state, "legacy "); + error = dev->bus->suspend(dev, state); + suspend_report_result(dev->bus->suspend, error); + } } - + End: up(&dev->sem); return error; @@ -349,67 +648,141 @@ static int suspend_device(struct device *dev, pm_message_t state) /** * dpm_suspend - Suspend every device. - * @state: Power state to put each device in. + * @state: PM transition of the system being carried out. * - * Walk the dpm_locked list. Suspend each device and move it - * to the dpm_off list. - * - * (For historical reasons, if it returns -EAGAIN, that used to mean - * that the device would be called again with interrupts disabled. - * These days, we use the "suspend_late()" callback for that, so we - * print a warning and consider it an error). + * Execute the appropriate "suspend" callbacks for all devices. */ static int dpm_suspend(pm_message_t state) { + struct list_head list; int error = 0; + INIT_LIST_HEAD(&list); mutex_lock(&dpm_list_mtx); - while (!list_empty(&dpm_active)) { - struct list_head *entry = dpm_active.prev; - struct device *dev = to_device(entry); - - WARN_ON(dev->parent && dev->parent->power.sleeping); + while (!list_empty(&dpm_list)) { + struct device *dev = to_device(dpm_list.prev); - dev->power.sleeping = true; + get_device(dev); mutex_unlock(&dpm_list_mtx); + error = suspend_device(dev, state); + mutex_lock(&dpm_list_mtx); if (error) { - printk(KERN_ERR "Could not suspend device %s: " - "error %d%s\n", - kobject_name(&dev->kobj), - error, - (error == -EAGAIN ? - " (please convert to suspend_late)" : - "")); - dev->power.sleeping = false; + pm_dev_err(dev, state, "", error); + put_device(dev); break; } + dev->power.status = DPM_OFF; if (!list_empty(&dev->power.entry)) - list_move(&dev->power.entry, &dpm_off); + list_move(&dev->power.entry, &list); + put_device(dev); } - if (!error) - all_sleeping = true; + list_splice(&list, dpm_list.prev); mutex_unlock(&dpm_list_mtx); + return error; +} + +/** + * prepare_device - Execute the ->prepare() callback(s) for given device. + * @dev: Device. + * @state: PM transition of the system being carried out. + */ +static int prepare_device(struct device *dev, pm_message_t state) +{ + int error = 0; + + down(&dev->sem); + + if (dev->bus && dev->bus->pm && dev->bus->pm->base.prepare) { + pm_dev_dbg(dev, state, "preparing "); + error = dev->bus->pm->base.prepare(dev); + suspend_report_result(dev->bus->pm->base.prepare, error); + if (error) + goto End; + } + + if (dev->type && dev->type->pm && dev->type->pm->prepare) { + pm_dev_dbg(dev, state, "preparing type "); + error = dev->type->pm->prepare(dev); + suspend_report_result(dev->type->pm->prepare, error); + if (error) + goto End; + } + + if (dev->class && dev->class->pm && dev->class->pm->prepare) { + pm_dev_dbg(dev, state, "preparing class "); + error = dev->class->pm->prepare(dev); + suspend_report_result(dev->class->pm->prepare, error); + } + End: + up(&dev->sem); + + return error; +} +/** + * dpm_prepare - Prepare all devices for a PM transition. + * @state: PM transition of the system being carried out. + * + * Execute the ->prepare() callback for all devices. + */ +static int dpm_prepare(pm_message_t state) +{ + struct list_head list; + int error = 0; + + INIT_LIST_HEAD(&list); + mutex_lock(&dpm_list_mtx); + transition_started = true; + while (!list_empty(&dpm_list)) { + struct device *dev = to_device(dpm_list.next); + + get_device(dev); + dev->power.status = DPM_PREPARING; + mutex_unlock(&dpm_list_mtx); + + error = prepare_device(dev, state); + + mutex_lock(&dpm_list_mtx); + if (error) { + dev->power.status = DPM_ON; + if (error == -EAGAIN) { + put_device(dev); + continue; + } + printk(KERN_ERR "PM: Failed to prepare device %s " + "for power transition: error %d\n", + kobject_name(&dev->kobj), error); + put_device(dev); + break; + } + dev->power.status = DPM_SUSPENDING; + if (!list_empty(&dev->power.entry)) + list_move_tail(&dev->power.entry, &list); + put_device(dev); + } + list_splice(&list, &dpm_list); + mutex_unlock(&dpm_list_mtx); return error; } /** * device_suspend - Save state and stop all devices in system. - * @state: new power management state + * @state: PM transition of the system being carried out. * - * Prevent new devices from being registered, then lock all devices - * and suspend them. + * Prepare and suspend all devices. */ int device_suspend(pm_message_t state) { int error; might_sleep(); - error = dpm_suspend(state); + error = dpm_prepare(state); + if (!error) + error = dpm_suspend(state); if (error) - device_resume(); + device_resume(resume_event(state)); return error; } EXPORT_SYMBOL_GPL(device_suspend); diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index a6894f2a4b99..a3252c0e2887 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -4,7 +4,7 @@ * main.c */ -extern struct list_head dpm_active; /* The active device list */ +extern struct list_head dpm_list; /* The active device list */ static inline struct device *to_device(struct list_head *entry) { diff --git a/drivers/base/power/trace.c b/drivers/base/power/trace.c index 2b4b392dcbc1..8c1e656b5f8b 100644 --- a/drivers/base/power/trace.c +++ b/drivers/base/power/trace.c @@ -188,9 +188,9 @@ static int show_file_hash(unsigned int value) static int show_dev_hash(unsigned int value) { int match = 0; - struct list_head * entry = dpm_active.prev; + struct list_head *entry = dpm_list.prev; - while (entry != &dpm_active) { + while (entry != &dpm_list) { struct device * dev = to_device(entry); unsigned int hash = hash_string(DEVSEED, dev->bus_id, DEVHASH); if (hash == value) { diff --git a/include/linux/device.h b/include/linux/device.h index 6a2d04c011bc..f71a78d123ae 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -68,6 +68,8 @@ struct bus_type { int (*resume_early)(struct device *dev); int (*resume)(struct device *dev); + struct pm_ext_ops *pm; + struct bus_type_private *p; }; @@ -131,6 +133,8 @@ struct device_driver { int (*resume) (struct device *dev); struct attribute_group **groups; + struct pm_ops *pm; + struct driver_private *p; }; @@ -197,6 +201,8 @@ struct class { int (*suspend)(struct device *dev, pm_message_t state); int (*resume)(struct device *dev); + + struct pm_ops *pm; }; extern int __must_check class_register(struct class *class); @@ -248,8 +254,11 @@ struct device_type { struct attribute_group **groups; int (*uevent)(struct device *dev, struct kobj_uevent_env *env); void (*release)(struct device *dev); + int (*suspend)(struct device *dev, pm_message_t state); int (*resume)(struct device *dev); + + struct pm_ops *pm; }; /* interface for exporting device attributes */ diff --git a/include/linux/pm.h b/include/linux/pm.h index 39a7ee859b67..4ad9de94449a 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -112,7 +112,9 @@ typedef struct pm_message { int event; } pm_message_t; -/* +/** + * struct pm_ops - device PM callbacks + * * Several driver power state transitions are externally visible, affecting * the state of pending I/O queues and (for drivers that touch hardware) * interrupts, wakeups, DMA, and other hardware state. There may also be @@ -120,6 +122,284 @@ typedef struct pm_message { * to the rest of the driver stack (such as a driver that's ON gating off * clocks which are not in active use). * + * The externally visible transitions are handled with the help of the following + * callbacks included in this structure: + * + * @prepare: Prepare the device for the upcoming transition, but do NOT change + * its hardware state. Prevent new children of the device from being + * registered after @prepare() returns (the driver's subsystem and + * generally the rest of the kernel is supposed to prevent new calls to the + * probe method from being made too once @prepare() has succeeded). If + * @prepare() detects a situation it cannot handle (e.g. registration of a + * child already in progress), it may return -EAGAIN, so that the PM core + * can execute it once again (e.g. after the new child has been registered) + * to recover from the race condition. This method is executed for all + * kinds of suspend transitions and is followed by one of the suspend + * callbacks: @suspend(), @freeze(), or @poweroff(). + * The PM core executes @prepare() for all devices before starting to + * execute suspend callbacks for any of them, so drivers may assume all of + * the other devices to be present and functional while @prepare() is being + * executed. In particular, it is safe to make GFP_KERNEL memory + * allocations from within @prepare(). However, drivers may NOT assume + * anything about the availability of the user space at that time and it + * is not correct to request firmware from within @prepare() (it's too + * late to do that). [To work around this limitation, drivers may + * register suspend and hibernation notifiers that are executed before the + * freezing of tasks.] + * + * @complete: Undo the changes made by @prepare(). This method is executed for + * all kinds of resume transitions, following one of the resume callbacks: + * @resume(), @thaw(), @restore(). Also called if the state transition + * fails before the driver's suspend callback (@suspend(), @freeze(), + * @poweroff()) can be executed (e.g. if the suspend callback fails for one + * of the other devices that the PM core has unsuccessfully attempted to + * suspend earlier). + * The PM core executes @complete() after it has executed the appropriate + * resume callback for all devices. + * + * @suspend: Executed before putting the system into a sleep state in which the + * contents of main memory are preserved. Quiesce the device, put it into + * a low power state appropriate for the upcoming system state (such as + * PCI_D3hot), and enable wakeup events as appropriate. + * + * @resume: Executed after waking the system up from a sleep state in which the + * contents of main memory were preserved. Put the device into the + * appropriate state, according to the information saved in memory by the + * preceding @suspend(). The driver starts working again, responding to + * hardware events and software requests. The hardware may have gone + * through a power-off reset, or it may have maintained state from the + * previous suspend() which the driver may rely on while resuming. On most + * platforms, there are no restrictions on availability of resources like + * clocks during @resume(). + * + * @freeze: Hibernation-specific, executed before creating a hibernation image. + * Quiesce operations so that a consistent image can be created, but do NOT + * otherwise put the device into a low power device state and do NOT emit + * system wakeup events. Save in main memory the device settings to be + * used by @restore() during the subsequent resume from hibernation or by + * the subsequent @thaw(), if the creation of the image or the restoration + * of main memory contents from it fails. + * + * @thaw: Hibernation-specific, executed after creating a hibernation image OR + * if the creation of the image fails. Also executed after a failing + * attempt to restore the contents of main memory from such an image. + * Undo the changes made by the preceding @freeze(), so the device can be + * operated in the same way as immediately before the call to @freeze(). + * + * @poweroff: Hibernation-specific, executed after saving a hibernation image. + * Quiesce the device, put it into a low power state appropriate for the + * upcoming system state (such as PCI_D3hot), and enable wakeup events as + * appropriate. + * + * @restore: Hibernation-specific, executed after restoring the contents of main + * memory from a hibernation image. Driver starts working again, + * responding to hardware events and software requests. Drivers may NOT + * make ANY assumptions about the hardware state right prior to @restore(). + * On most platforms, there are no restrictions on availability of + * resources like clocks during @restore(). + * + * All of the above callbacks, except for @complete(), return error codes. + * However, the error codes returned by the resume operations, @resume(), + * @thaw(), and @restore(), do not cause the PM core to abort the resume + * transition during which they are returned. The error codes returned in + * that cases are only printed by the PM core to the system logs for debugging + * purposes. Still, it is recommended that drivers only return error codes + * from their resume methods in case of an unrecoverable failure (i.e. when the + * device being handled refuses to resume and becomes unusable) to allow us to + * modify the PM core in the future, so that it can avoid attempting to handle + * devices that failed to resume and their children. + * + * It is allowed to unregister devices while the above callbacks are being + * executed. However, it is not allowed to unregister a device from within any + * of its own callbacks. + */ + +struct pm_ops { + int (*prepare)(struct device *dev); + void (*complete)(struct device *dev); + int (*suspend)(struct device *dev); + int (*resume)(struct device *dev); + int (*freeze)(struct device *dev); + int (*thaw)(struct device *dev); + int (*poweroff)(struct device *dev); + int (*restore)(struct device *dev); +}; + +/** + * struct pm_ext_ops - extended device PM callbacks + * + * Some devices require certain operations related to suspend and hibernation + * to be carried out with interrupts disabled. Thus, 'struct pm_ext_ops' below + * is defined, adding callbacks to be executed with interrupts disabled to + * 'struct pm_ops'. + * + * The following callbacks included in 'struct pm_ext_ops' are executed with + * the nonboot CPUs switched off and with interrupts disabled on the only + * functional CPU. They also are executed with the PM core list of devices + * locked, so they must NOT unregister any devices. + * + * @suspend_noirq: Complete the operations of ->suspend() by carrying out any + * actions required for suspending the device that need interrupts to be + * disabled + * + * @resume_noirq: Prepare for the execution of ->resume() by carrying out any + * actions required for resuming the device that need interrupts to be + * disabled + * + * @freeze_noirq: Complete the operations of ->freeze() by carrying out any + * actions required for freezing the device that need interrupts to be + * disabled + * + * @thaw_noirq: Prepare for the execution of ->thaw() by carrying out any + * actions required for thawing the device that need interrupts to be + * disabled + * + * @poweroff_noirq: Complete the operations of ->poweroff() by carrying out any + * actions required for handling the device that need interrupts to be + * disabled + * + * @restore_noirq: Prepare for the execution of ->restore() by carrying out any + * actions required for restoring the operations of the device that need + * interrupts to be disabled + * + * All of the above callbacks return error codes, but the error codes returned + * by the resume operations, @resume_noirq(), @thaw_noirq(), and + * @restore_noirq(), do not cause the PM core to abort the resume transition + * during which they are returned. The error codes returned in that cases are + * only printed by the PM core to the system logs for debugging purposes. + * Still, as stated above, it is recommended that drivers only return error + * codes from their resume methods if the device being handled fails to resume + * and is not usable any more. + */ + +struct pm_ext_ops { + struct pm_ops base; + int (*suspend_noirq)(struct device *dev); + int (*resume_noirq)(struct device *dev); + int (*freeze_noirq)(struct device *dev); + int (*thaw_noirq)(struct device *dev); + int (*poweroff_noirq)(struct device *dev); + int (*restore_noirq)(struct device *dev); +}; + +/** + * PM_EVENT_ messages + * + * The following PM_EVENT_ messages are defined for the internal use of the PM + * core, in order to provide a mechanism allowing the high level suspend and + * hibernation code to convey the necessary information to the device PM core + * code: + * + * ON No transition. + * + * FREEZE System is going to hibernate, call ->prepare() and ->freeze() + * for all devices. + * + * SUSPEND System is going to suspend, call ->prepare() and ->suspend() + * for all devices. + * + * HIBERNATE Hibernation image has been saved, call ->prepare() and + * ->poweroff() for all devices. + * + * QUIESCE Contents of main memory are going to be restored from a (loaded) + * hibernation image, call ->prepare() and ->freeze() for all + * devices. + * + * RESUME System is resuming, call ->resume() and ->complete() for all + * devices. + * + * THAW Hibernation image has been created, call ->thaw() and + * ->complete() for all devices. + * + * RESTORE Contents of main memory have been restored from a hibernation + * image, call ->restore() and ->complete() for all devices. + * + * RECOVER Creation of a hibernation image or restoration of the main + * memory contents from a hibernation image has failed, call + * ->thaw() and ->complete() for all devices. + */ + +#define PM_EVENT_ON 0x0000 +#define PM_EVENT_FREEZE 0x0001 +#define PM_EVENT_SUSPEND 0x0002 +#define PM_EVENT_HIBERNATE 0x0004 +#define PM_EVENT_QUIESCE 0x0008 +#define PM_EVENT_RESUME 0x0010 +#define PM_EVENT_THAW 0x0020 +#define PM_EVENT_RESTORE 0x0040 +#define PM_EVENT_RECOVER 0x0080 + +#define PM_EVENT_SLEEP (PM_EVENT_SUSPEND | PM_EVENT_HIBERNATE) + +#define PMSG_FREEZE ((struct pm_message){ .event = PM_EVENT_FREEZE, }) +#define PMSG_QUIESCE ((struct pm_message){ .event = PM_EVENT_QUIESCE, }) +#define PMSG_SUSPEND ((struct pm_message){ .event = PM_EVENT_SUSPEND, }) +#define PMSG_HIBERNATE ((struct pm_message){ .event = PM_EVENT_HIBERNATE, }) +#define PMSG_RESUME ((struct pm_message){ .event = PM_EVENT_RESUME, }) +#define PMSG_THAW ((struct pm_message){ .event = PM_EVENT_THAW, }) +#define PMSG_RESTORE ((struct pm_message){ .event = PM_EVENT_RESTORE, }) +#define PMSG_RECOVER ((struct pm_message){ .event = PM_EVENT_RECOVER, }) +#define PMSG_ON ((struct pm_message){ .event = PM_EVENT_ON, }) + +/** + * Device power management states + * + * These state labels are used internally by the PM core to indicate the current + * status of a device with respect to the PM core operations. + * + * DPM_ON Device is regarded as operational. Set this way + * initially and when ->complete() is about to be called. + * Also set when ->prepare() fails. + * + * DPM_PREPARING Device is going to be prepared for a PM transition. Set + * when ->prepare() is about to be called. + * + * DPM_RESUMING Device is going to be resumed. Set when ->resume(), + * ->thaw(), or ->restore() is about to be called. + * + * DPM_SUSPENDING Device has been prepared for a power transition. Set + * when ->prepare() has just succeeded. + * + * DPM_OFF Device is regarded as inactive. Set immediately after + * ->suspend(), ->freeze(), or ->poweroff() has succeeded. + * Also set when ->resume()_noirq, ->thaw_noirq(), or + * ->restore_noirq() is about to be called. + * + * DPM_OFF_IRQ Device is in a "deep sleep". Set immediately after + * ->suspend_noirq(), ->freeze_noirq(), or + * ->poweroff_noirq() has just succeeded. + */ + +enum dpm_state { + DPM_INVALID, + DPM_ON, + DPM_PREPARING, + DPM_RESUMING, + DPM_SUSPENDING, + DPM_OFF, + DPM_OFF_IRQ, +}; + +struct dev_pm_info { + pm_message_t power_state; + unsigned can_wakeup:1; + unsigned should_wakeup:1; + enum dpm_state status; /* Owned by the PM core */ +#ifdef CONFIG_PM_SLEEP + struct list_head entry; +#endif +}; + +/* + * The PM_EVENT_ messages are also used by drivers implementing the legacy + * suspend framework, based on the ->suspend() and ->resume() callbacks common + * for suspend and hibernation transitions, according to the rules below. + */ + +/* Necessary, because several drivers use PM_EVENT_PRETHAW */ +#define PM_EVENT_PRETHAW PM_EVENT_QUIESCE + +/* * One transition is triggered by resume(), after a suspend() call; the * message is implicit: * @@ -164,35 +444,13 @@ typedef struct pm_message { * or from system low-power states such as standby or suspend-to-RAM. */ -#define PM_EVENT_ON 0 -#define PM_EVENT_FREEZE 1 -#define PM_EVENT_SUSPEND 2 -#define PM_EVENT_HIBERNATE 4 -#define PM_EVENT_PRETHAW 8 - -#define PM_EVENT_SLEEP (PM_EVENT_SUSPEND | PM_EVENT_HIBERNATE) - -#define PMSG_FREEZE ((struct pm_message){ .event = PM_EVENT_FREEZE, }) -#define PMSG_PRETHAW ((struct pm_message){ .event = PM_EVENT_PRETHAW, }) -#define PMSG_SUSPEND ((struct pm_message){ .event = PM_EVENT_SUSPEND, }) -#define PMSG_HIBERNATE ((struct pm_message){ .event = PM_EVENT_HIBERNATE, }) -#define PMSG_ON ((struct pm_message){ .event = PM_EVENT_ON, }) - -struct dev_pm_info { - pm_message_t power_state; - unsigned can_wakeup:1; - unsigned should_wakeup:1; - bool sleeping:1; /* Owned by the PM core */ -#ifdef CONFIG_PM_SLEEP - struct list_head entry; -#endif -}; +#ifdef CONFIG_PM_SLEEP +extern void device_pm_lock(void); +extern void device_power_up(pm_message_t state); +extern void device_resume(pm_message_t state); +extern void device_pm_unlock(void); extern int device_power_down(pm_message_t state); -extern void device_power_up(void); -extern void device_resume(void); - -#ifdef CONFIG_PM_SLEEP extern int device_suspend(pm_message_t state); extern int device_prepare_suspend(pm_message_t state); diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 14a656cdc652..d416be0efa8a 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -193,6 +193,7 @@ static int create_image(int platform_mode) if (error) return error; + device_pm_lock(); local_irq_disable(); /* At this point, device_suspend() has been called, but *not* * device_power_down(). We *must* call device_power_down() now. @@ -224,9 +225,11 @@ static int create_image(int platform_mode) /* NOTE: device_power_up() is just a resume() for devices * that suspended with irqs off ... no overall powerup. */ - device_power_up(); + device_power_up(in_suspend ? + (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); Enable_irqs: local_irq_enable(); + device_pm_unlock(); return error; } @@ -280,7 +283,8 @@ int hibernation_snapshot(int platform_mode) Finish: platform_finish(platform_mode); Resume_devices: - device_resume(); + device_resume(in_suspend ? + (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); Resume_console: resume_console(); Close: @@ -300,8 +304,9 @@ static int resume_target_kernel(void) { int error; + device_pm_lock(); local_irq_disable(); - error = device_power_down(PMSG_PRETHAW); + error = device_power_down(PMSG_QUIESCE); if (error) { printk(KERN_ERR "PM: Some devices failed to power down, " "aborting resume\n"); @@ -329,9 +334,10 @@ static int resume_target_kernel(void) swsusp_free(); restore_processor_state(); touch_softlockup_watchdog(); - device_power_up(); + device_power_up(PMSG_RECOVER); Enable_irqs: local_irq_enable(); + device_pm_unlock(); return error; } @@ -350,7 +356,7 @@ int hibernation_restore(int platform_mode) pm_prepare_console(); suspend_console(); - error = device_suspend(PMSG_PRETHAW); + error = device_suspend(PMSG_QUIESCE); if (error) goto Finish; @@ -362,7 +368,7 @@ int hibernation_restore(int platform_mode) enable_nonboot_cpus(); } platform_restore_cleanup(platform_mode); - device_resume(); + device_resume(PMSG_RECOVER); Finish: resume_console(); pm_restore_console(); @@ -403,6 +409,7 @@ int hibernation_platform_enter(void) if (error) goto Finish; + device_pm_lock(); local_irq_disable(); error = device_power_down(PMSG_HIBERNATE); if (!error) { @@ -411,6 +418,7 @@ int hibernation_platform_enter(void) while (1); } local_irq_enable(); + device_pm_unlock(); /* * We don't need to reenable the nonboot CPUs or resume consoles, since @@ -419,7 +427,7 @@ int hibernation_platform_enter(void) Finish: hibernation_ops->finish(); Resume_devices: - device_resume(); + device_resume(PMSG_RESTORE); Resume_console: resume_console(); Close: diff --git a/kernel/power/main.c b/kernel/power/main.c index 6a6d5eb3524e..d023b6b584e5 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -228,6 +228,7 @@ static int suspend_enter(suspend_state_t state) { int error = 0; + device_pm_lock(); arch_suspend_disable_irqs(); BUG_ON(!irqs_disabled()); @@ -239,10 +240,11 @@ static int suspend_enter(suspend_state_t state) if (!suspend_test(TEST_CORE)) error = suspend_ops->enter(state); - device_power_up(); + device_power_up(PMSG_RESUME); Done: arch_suspend_enable_irqs(); BUG_ON(irqs_disabled()); + device_pm_unlock(); return error; } @@ -291,7 +293,7 @@ int suspend_devices_and_enter(suspend_state_t state) if (suspend_ops->finish) suspend_ops->finish(); Resume_devices: - device_resume(); + device_resume(PMSG_RESUME); Resume_console: resume_console(); Close: -- cgit v1.2.3 From bbb44d9f23d868a2837c6b22b8dfb123d8e7800c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 20 May 2008 00:49:04 +0200 Subject: PCI: implement new suspend/resume callbacks Implement new suspend and hibernation callbacks for the PCI bus type. Signed-off-by: Rafael J. Wysocki Acked-by: Pavel Machek Signed-off-by: Jesse Barnes --- drivers/pci/pci-driver.c | 392 +++++++++++++++++++++++++++++++++++++++++------ include/linux/pci.h | 2 +- 2 files changed, 347 insertions(+), 47 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 677fd9d6db12..8eb8a3091dc7 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -274,7 +274,57 @@ static int pci_device_remove(struct device * dev) return 0; } -static int pci_device_suspend(struct device * dev, pm_message_t state) +static void pci_device_shutdown(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct pci_driver *drv = pci_dev->driver; + + if (drv && drv->shutdown) + drv->shutdown(pci_dev); + pci_msi_shutdown(pci_dev); + pci_msix_shutdown(pci_dev); +} + +#ifdef CONFIG_PM_SLEEP + +/* + * Default "suspend" method for devices that have no driver provided suspend, + * or not even a driver at all. + */ +static void pci_default_pm_suspend(struct pci_dev *pci_dev) +{ + pci_save_state(pci_dev); + /* + * mark its power state as "unknown", since we don't know if + * e.g. the BIOS will change its device state when we suspend. + */ + if (pci_dev->current_state == PCI_D0) + pci_dev->current_state = PCI_UNKNOWN; +} + +/* + * Default "resume" method for devices that have no driver provided resume, + * or not even a driver at all. + */ +static int pci_default_pm_resume(struct pci_dev *pci_dev) +{ + int retval = 0; + + /* restore the PCI config space */ + pci_restore_state(pci_dev); + /* if the device was enabled before suspend, reenable */ + retval = pci_reenable_device(pci_dev); + /* + * if the device was busmaster before the suspend, make it busmaster + * again + */ + if (pci_dev->is_busmaster) + pci_set_master(pci_dev); + + return retval; +} + +static int pci_legacy_suspend(struct device *dev, pm_message_t state) { struct pci_dev * pci_dev = to_pci_dev(dev); struct pci_driver * drv = pci_dev->driver; @@ -284,21 +334,12 @@ static int pci_device_suspend(struct device * dev, pm_message_t state) i = drv->suspend(pci_dev, state); suspend_report_result(drv->suspend, i); } else { - pci_save_state(pci_dev); - /* - * mark its power state as "unknown", since we don't know if - * e.g. the BIOS will change its device state when we suspend. - */ - if (pci_dev->current_state == PCI_D0) - pci_dev->current_state = PCI_UNKNOWN; + pci_default_pm_suspend(pci_dev); } - - pci_fixup_device(pci_fixup_suspend, pci_dev); - return i; } -static int pci_device_suspend_late(struct device * dev, pm_message_t state) +static int pci_legacy_suspend_late(struct device *dev, pm_message_t state) { struct pci_dev * pci_dev = to_pci_dev(dev); struct pci_driver * drv = pci_dev->driver; @@ -311,26 +352,7 @@ static int pci_device_suspend_late(struct device * dev, pm_message_t state) return i; } -/* - * Default resume method for devices that have no driver provided resume, - * or not even a driver at all. - */ -static int pci_default_resume(struct pci_dev *pci_dev) -{ - int retval = 0; - - /* restore the PCI config space */ - pci_restore_state(pci_dev); - /* if the device was enabled before suspend, reenable */ - retval = pci_reenable_device(pci_dev); - /* if the device was busmaster before the suspend, make it busmaster again */ - if (pci_dev->is_busmaster) - pci_set_master(pci_dev); - - return retval; -} - -static int pci_device_resume(struct device * dev) +static int pci_legacy_resume(struct device *dev) { int error; struct pci_dev * pci_dev = to_pci_dev(dev); @@ -339,35 +361,313 @@ static int pci_device_resume(struct device * dev) if (drv && drv->resume) error = drv->resume(pci_dev); else - error = pci_default_resume(pci_dev); - pci_fixup_device(pci_fixup_resume, pci_dev); + error = pci_default_pm_resume(pci_dev); return error; } -static int pci_device_resume_early(struct device * dev) +static int pci_legacy_resume_early(struct device *dev) { int error = 0; struct pci_dev * pci_dev = to_pci_dev(dev); struct pci_driver * drv = pci_dev->driver; - pci_fixup_device(pci_fixup_resume_early, pci_dev); - if (drv && drv->resume_early) error = drv->resume_early(pci_dev); return error; } -static void pci_device_shutdown(struct device *dev) +static int pci_pm_prepare(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int error = 0; + + if (drv && drv->pm && drv->pm->prepare) + error = drv->pm->prepare(dev); + + return error; +} + +static void pci_pm_complete(struct device *dev) +{ + struct device_driver *drv = dev->driver; + + if (drv && drv->pm && drv->pm->complete) + drv->pm->complete(dev); +} + +#ifdef CONFIG_SUSPEND + +static int pci_pm_suspend(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct device_driver *drv = dev->driver; + int error = 0; + + if (drv && drv->pm) { + if (drv->pm->suspend) { + error = drv->pm->suspend(dev); + suspend_report_result(drv->pm->suspend, error); + } else { + pci_default_pm_suspend(pci_dev); + } + } else { + error = pci_legacy_suspend(dev, PMSG_SUSPEND); + } + pci_fixup_device(pci_fixup_suspend, pci_dev); + + return error; +} + +static int pci_pm_suspend_noirq(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); struct pci_driver *drv = pci_dev->driver; + int error = 0; - if (drv && drv->shutdown) - drv->shutdown(pci_dev); - pci_msi_shutdown(pci_dev); - pci_msix_shutdown(pci_dev); + if (drv && drv->pm) { + if (drv->pm->suspend_noirq) { + error = drv->pm->suspend_noirq(dev); + suspend_report_result(drv->pm->suspend_noirq, error); + } + } else { + error = pci_legacy_suspend_late(dev, PMSG_SUSPEND); + } + + return error; +} + +static int pci_pm_resume(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct device_driver *drv = dev->driver; + int error; + + pci_fixup_device(pci_fixup_resume, pci_dev); + + if (drv && drv->pm) { + error = drv->pm->resume ? drv->pm->resume(dev) : + pci_default_pm_resume(pci_dev); + } else { + error = pci_legacy_resume(dev); + } + + return error; +} + +static int pci_pm_resume_noirq(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct pci_driver *drv = pci_dev->driver; + int error = 0; + + pci_fixup_device(pci_fixup_resume_early, pci_dev); + + if (drv && drv->pm) { + if (drv->pm->resume_noirq) + error = drv->pm->resume_noirq(dev); + } else { + error = pci_legacy_resume_early(dev); + } + + return error; +} + +#else /* !CONFIG_SUSPEND */ + +#define pci_pm_suspend NULL +#define pci_pm_suspend_noirq NULL +#define pci_pm_resume NULL +#define pci_pm_resume_noirq NULL + +#endif /* !CONFIG_SUSPEND */ + +#ifdef CONFIG_HIBERNATION + +static int pci_pm_freeze(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct device_driver *drv = dev->driver; + int error = 0; + + if (drv && drv->pm) { + if (drv->pm->freeze) { + error = drv->pm->freeze(dev); + suspend_report_result(drv->pm->freeze, error); + } else { + pci_default_pm_suspend(pci_dev); + } + } else { + error = pci_legacy_suspend(dev, PMSG_FREEZE); + pci_fixup_device(pci_fixup_suspend, pci_dev); + } + + return error; +} + +static int pci_pm_freeze_noirq(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct pci_driver *drv = pci_dev->driver; + int error = 0; + + if (drv && drv->pm) { + if (drv->pm->freeze_noirq) { + error = drv->pm->freeze_noirq(dev); + suspend_report_result(drv->pm->freeze_noirq, error); + } + } else { + error = pci_legacy_suspend_late(dev, PMSG_FREEZE); + } + + return error; +} + +static int pci_pm_thaw(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int error = 0; + + if (drv && drv->pm) { + if (drv->pm->thaw) + error = drv->pm->thaw(dev); + } else { + pci_fixup_device(pci_fixup_resume, to_pci_dev(dev)); + error = pci_legacy_resume(dev); + } + + return error; +} + +static int pci_pm_thaw_noirq(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct pci_driver *drv = pci_dev->driver; + int error = 0; + + if (drv && drv->pm) { + if (drv->pm->thaw_noirq) + error = drv->pm->thaw_noirq(dev); + } else { + pci_fixup_device(pci_fixup_resume_early, pci_dev); + error = pci_legacy_resume_early(dev); + } + + return error; +} + +static int pci_pm_poweroff(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int error = 0; + + pci_fixup_device(pci_fixup_suspend, to_pci_dev(dev)); + + if (drv && drv->pm) { + if (drv->pm->poweroff) { + error = drv->pm->poweroff(dev); + suspend_report_result(drv->pm->poweroff, error); + } + } else { + error = pci_legacy_suspend(dev, PMSG_HIBERNATE); + } + + return error; +} + +static int pci_pm_poweroff_noirq(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct pci_driver *drv = pci_dev->driver; + int error = 0; + + if (drv && drv->pm) { + if (drv->pm->poweroff_noirq) { + error = drv->pm->poweroff_noirq(dev); + suspend_report_result(drv->pm->poweroff_noirq, error); + } + } else { + error = pci_legacy_suspend_late(dev, PMSG_HIBERNATE); + } + + return error; +} + +static int pci_pm_restore(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct device_driver *drv = dev->driver; + int error; + + if (drv && drv->pm) { + error = drv->pm->restore ? drv->pm->restore(dev) : + pci_default_pm_resume(pci_dev); + } else { + error = pci_legacy_resume(dev); + } + pci_fixup_device(pci_fixup_resume, pci_dev); + + return error; +} + +static int pci_pm_restore_noirq(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct pci_driver *drv = pci_dev->driver; + int error = 0; + + pci_fixup_device(pci_fixup_resume, pci_dev); + + if (drv && drv->pm) { + if (drv->pm->restore_noirq) + error = drv->pm->restore_noirq(dev); + } else { + error = pci_legacy_resume_early(dev); + } + pci_fixup_device(pci_fixup_resume_early, pci_dev); + + return error; } +#else /* !CONFIG_HIBERNATION */ + +#define pci_pm_freeze NULL +#define pci_pm_freeze_noirq NULL +#define pci_pm_thaw NULL +#define pci_pm_thaw_noirq NULL +#define pci_pm_poweroff NULL +#define pci_pm_poweroff_noirq NULL +#define pci_pm_restore NULL +#define pci_pm_restore_noirq NULL + +#endif /* !CONFIG_HIBERNATION */ + +struct pm_ext_ops pci_pm_ops = { + .base = { + .prepare = pci_pm_prepare, + .complete = pci_pm_complete, + .suspend = pci_pm_suspend, + .resume = pci_pm_resume, + .freeze = pci_pm_freeze, + .thaw = pci_pm_thaw, + .poweroff = pci_pm_poweroff, + .restore = pci_pm_restore, + }, + .suspend_noirq = pci_pm_suspend_noirq, + .resume_noirq = pci_pm_resume_noirq, + .freeze_noirq = pci_pm_freeze_noirq, + .thaw_noirq = pci_pm_thaw_noirq, + .poweroff_noirq = pci_pm_poweroff_noirq, + .restore_noirq = pci_pm_restore_noirq, +}; + +#define PCI_PM_OPS_PTR &pci_pm_ops + +#else /* !CONFIG_PM_SLEEP */ + +#define PCI_PM_OPS_PTR NULL + +#endif /* !CONFIG_PM_SLEEP */ + /** * __pci_register_driver - register a new pci driver * @drv: the driver structure to register @@ -390,6 +690,9 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner, drv->driver.owner = owner; drv->driver.mod_name = mod_name; + if (drv->pm) + drv->driver.pm = &drv->pm->base; + spin_lock_init(&drv->dynids.lock); INIT_LIST_HEAD(&drv->dynids.list); @@ -515,12 +818,9 @@ struct bus_type pci_bus_type = { .uevent = pci_uevent, .probe = pci_device_probe, .remove = pci_device_remove, - .suspend = pci_device_suspend, - .suspend_late = pci_device_suspend_late, - .resume_early = pci_device_resume_early, - .resume = pci_device_resume, .shutdown = pci_device_shutdown, .dev_attrs = pci_dev_attrs, + .pm = PCI_PM_OPS_PTR, }; static int __init pci_driver_init(void) diff --git a/include/linux/pci.h b/include/linux/pci.h index 700704ef70f3..507ee52323cd 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -389,7 +389,7 @@ struct pci_driver { int (*resume_early) (struct pci_dev *dev); int (*resume) (struct pci_dev *dev); /* Device woken up */ void (*shutdown) (struct pci_dev *dev); - + struct pm_ext_ops *pm; struct pci_error_handlers *err_handler; struct device_driver driver; struct pci_dynids dynids; -- cgit v1.2.3 From 25e18499e08cb097cbbfeab5de25d094d5312ee5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 21 May 2008 01:40:43 +0200 Subject: Implement new suspend and hibernation callbacks for platform busses Implement new suspend and hibernation callbacks for the platform bus type. Signed-off-by: Rafael J. Wysocki Acked-by: Pavel Machek Signed-off-by: Greg KH Signed-off-by: Jesse Barnes --- drivers/base/platform.c | 296 ++++++++++++++++++++++++++++++++++++++-- include/linux/platform_device.h | 1 + 2 files changed, 289 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 911ec600fe71..3f940393d6c7 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -453,6 +453,8 @@ int platform_driver_register(struct platform_driver *drv) drv->driver.suspend = platform_drv_suspend; if (drv->resume) drv->driver.resume = platform_drv_resume; + if (drv->pm) + drv->driver.pm = &drv->pm->base; return driver_register(&drv->driver); } EXPORT_SYMBOL_GPL(platform_driver_register); @@ -560,7 +562,9 @@ static int platform_match(struct device *dev, struct device_driver *drv) return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0); } -static int platform_suspend(struct device *dev, pm_message_t mesg) +#ifdef CONFIG_PM_SLEEP + +static int platform_legacy_suspend(struct device *dev, pm_message_t mesg) { int ret = 0; @@ -570,7 +574,7 @@ static int platform_suspend(struct device *dev, pm_message_t mesg) return ret; } -static int platform_suspend_late(struct device *dev, pm_message_t mesg) +static int platform_legacy_suspend_late(struct device *dev, pm_message_t mesg) { struct platform_driver *drv = to_platform_driver(dev->driver); struct platform_device *pdev; @@ -583,7 +587,7 @@ static int platform_suspend_late(struct device *dev, pm_message_t mesg) return ret; } -static int platform_resume_early(struct device *dev) +static int platform_legacy_resume_early(struct device *dev) { struct platform_driver *drv = to_platform_driver(dev->driver); struct platform_device *pdev; @@ -596,7 +600,7 @@ static int platform_resume_early(struct device *dev) return ret; } -static int platform_resume(struct device *dev) +static int platform_legacy_resume(struct device *dev) { int ret = 0; @@ -606,15 +610,291 @@ static int platform_resume(struct device *dev) return ret; } +static int platform_pm_prepare(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (drv && drv->pm && drv->pm->prepare) + ret = drv->pm->prepare(dev); + + return ret; +} + +static void platform_pm_complete(struct device *dev) +{ + struct device_driver *drv = dev->driver; + + if (drv && drv->pm && drv->pm->complete) + drv->pm->complete(dev); +} + +#ifdef CONFIG_SUSPEND + +static int platform_pm_suspend(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (drv && drv->pm) { + if (drv->pm->suspend) + ret = drv->pm->suspend(dev); + } else { + ret = platform_legacy_suspend(dev, PMSG_SUSPEND); + } + + return ret; +} + +static int platform_pm_suspend_noirq(struct device *dev) +{ + struct platform_driver *pdrv; + int ret = 0; + + if (!dev->driver) + return 0; + + pdrv = to_platform_driver(dev->driver); + if (pdrv->pm) { + if (pdrv->pm->suspend_noirq) + ret = pdrv->pm->suspend_noirq(dev); + } else { + ret = platform_legacy_suspend_late(dev, PMSG_SUSPEND); + } + + return ret; +} + +static int platform_pm_resume(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (drv && drv->pm) { + if (drv->pm->resume) + ret = drv->pm->resume(dev); + } else { + ret = platform_legacy_resume(dev); + } + + return ret; +} + +static int platform_pm_resume_noirq(struct device *dev) +{ + struct platform_driver *pdrv; + int ret = 0; + + if (!dev->driver) + return 0; + + pdrv = to_platform_driver(dev->driver); + if (pdrv->pm) { + if (pdrv->pm->resume_noirq) + ret = pdrv->pm->resume_noirq(dev); + } else { + ret = platform_legacy_resume_early(dev); + } + + return ret; +} + +#else /* !CONFIG_SUSPEND */ + +#define platform_pm_suspend NULL +#define platform_pm_resume NULL +#define platform_pm_suspend_noirq NULL +#define platform_pm_resume_noirq NULL + +#endif /* !CONFIG_SUSPEND */ + +#ifdef CONFIG_HIBERNATION + +static int platform_pm_freeze(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (!drv) + return 0; + + if (drv->pm) { + if (drv->pm->freeze) + ret = drv->pm->freeze(dev); + } else { + ret = platform_legacy_suspend(dev, PMSG_FREEZE); + } + + return ret; +} + +static int platform_pm_freeze_noirq(struct device *dev) +{ + struct platform_driver *pdrv; + int ret = 0; + + if (!dev->driver) + return 0; + + pdrv = to_platform_driver(dev->driver); + if (pdrv->pm) { + if (pdrv->pm->freeze_noirq) + ret = pdrv->pm->freeze_noirq(dev); + } else { + ret = platform_legacy_suspend_late(dev, PMSG_FREEZE); + } + + return ret; +} + +static int platform_pm_thaw(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (drv && drv->pm) { + if (drv->pm->thaw) + ret = drv->pm->thaw(dev); + } else { + ret = platform_legacy_resume(dev); + } + + return ret; +} + +static int platform_pm_thaw_noirq(struct device *dev) +{ + struct platform_driver *pdrv; + int ret = 0; + + if (!dev->driver) + return 0; + + pdrv = to_platform_driver(dev->driver); + if (pdrv->pm) { + if (pdrv->pm->thaw_noirq) + ret = pdrv->pm->thaw_noirq(dev); + } else { + ret = platform_legacy_resume_early(dev); + } + + return ret; +} + +static int platform_pm_poweroff(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (drv && drv->pm) { + if (drv->pm->poweroff) + ret = drv->pm->poweroff(dev); + } else { + ret = platform_legacy_suspend(dev, PMSG_HIBERNATE); + } + + return ret; +} + +static int platform_pm_poweroff_noirq(struct device *dev) +{ + struct platform_driver *pdrv; + int ret = 0; + + if (!dev->driver) + return 0; + + pdrv = to_platform_driver(dev->driver); + if (pdrv->pm) { + if (pdrv->pm->poweroff_noirq) + ret = pdrv->pm->poweroff_noirq(dev); + } else { + ret = platform_legacy_suspend_late(dev, PMSG_HIBERNATE); + } + + return ret; +} + +static int platform_pm_restore(struct device *dev) +{ + struct device_driver *drv = dev->driver; + int ret = 0; + + if (drv && drv->pm) { + if (drv->pm->restore) + ret = drv->pm->restore(dev); + } else { + ret = platform_legacy_resume(dev); + } + + return ret; +} + +static int platform_pm_restore_noirq(struct device *dev) +{ + struct platform_driver *pdrv; + int ret = 0; + + if (!dev->driver) + return 0; + + pdrv = to_platform_driver(dev->driver); + if (pdrv->pm) { + if (pdrv->pm->restore_noirq) + ret = pdrv->pm->restore_noirq(dev); + } else { + ret = platform_legacy_resume_early(dev); + } + + return ret; +} + +#else /* !CONFIG_HIBERNATION */ + +#define platform_pm_freeze NULL +#define platform_pm_thaw NULL +#define platform_pm_poweroff NULL +#define platform_pm_restore NULL +#define platform_pm_freeze_noirq NULL +#define platform_pm_thaw_noirq NULL +#define platform_pm_poweroff_noirq NULL +#define platform_pm_restore_noirq NULL + +#endif /* !CONFIG_HIBERNATION */ + +struct pm_ext_ops platform_pm_ops = { + .base = { + .prepare = platform_pm_prepare, + .complete = platform_pm_complete, + .suspend = platform_pm_suspend, + .resume = platform_pm_resume, + .freeze = platform_pm_freeze, + .thaw = platform_pm_thaw, + .poweroff = platform_pm_poweroff, + .restore = platform_pm_restore, + }, + .suspend_noirq = platform_pm_suspend_noirq, + .resume_noirq = platform_pm_resume_noirq, + .freeze_noirq = platform_pm_freeze_noirq, + .thaw_noirq = platform_pm_thaw_noirq, + .poweroff_noirq = platform_pm_poweroff_noirq, + .restore_noirq = platform_pm_restore_noirq, +}; + +#define PLATFORM_PM_OPS_PTR &platform_pm_ops + +#else /* !CONFIG_PM_SLEEP */ + +#define PLATFORM_PM_OPS_PTR NULL + +#endif /* !CONFIG_PM_SLEEP */ + struct bus_type platform_bus_type = { .name = "platform", .dev_attrs = platform_dev_attrs, .match = platform_match, .uevent = platform_uevent, - .suspend = platform_suspend, - .suspend_late = platform_suspend_late, - .resume_early = platform_resume_early, - .resume = platform_resume, + .pm = PLATFORM_PM_OPS_PTR, }; EXPORT_SYMBOL_GPL(platform_bus_type); diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index 3261681c82a4..95ac21ab3a09 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h @@ -53,6 +53,7 @@ struct platform_driver { int (*suspend_late)(struct platform_device *, pm_message_t state); int (*resume_early)(struct platform_device *); int (*resume)(struct platform_device *); + struct pm_ext_ops *pm; struct device_driver driver; }; -- cgit v1.2.3 From ac9c052d10d8d6f46a30cb46c0d6d753997c299f Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Wed, 28 May 2008 15:01:03 +0900 Subject: shpchp: check firmware before taking control Fix the following problems of shpchp driver about getting hotplug control from firmware. - The shpchp driver must not control the hotplug controller if it fails to get control from the firmware. But current shpchp controls the hotplug controller regardless the result, because it doesn't check the return value of get_hp_hw_control_from_firmware(). - Current shpchp driver doesn't support _OSC. The pciehp driver already have the code for evaluating _OSC and OSHP and shpchp and pciehp can share it. So this patch move that code from pciehp to acpi_pcihp.c. Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/acpi_pcihp.c | 85 +++++++++++++++++++++++++++++++++++++-- drivers/pci/hotplug/pciehp.h | 10 +++-- drivers/pci/hotplug/pciehp_hpc.c | 69 ------------------------------- drivers/pci/hotplug/shpchp.h | 14 ++++--- drivers/pci/hotplug/shpchp_core.c | 15 +++---- drivers/pci/hotplug/shpchp_hpc.c | 1 - include/linux/pci_hotplug.h | 2 +- 7 files changed, 105 insertions(+), 91 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c index f8c187a763bd..93e37f0666ab 100644 --- a/drivers/pci/hotplug/acpi_pcihp.c +++ b/drivers/pci/hotplug/acpi_pcihp.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -299,7 +300,7 @@ free_and_return: * * @handle - the handle of the hotplug controller. */ -acpi_status acpi_run_oshp(acpi_handle handle) +static acpi_status acpi_run_oshp(acpi_handle handle) { acpi_status status; struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -322,9 +323,6 @@ acpi_status acpi_run_oshp(acpi_handle handle) kfree(string.pointer); return status; } -EXPORT_SYMBOL_GPL(acpi_run_oshp); - - /* acpi_get_hp_params_from_firmware * @@ -374,6 +372,85 @@ acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus, } EXPORT_SYMBOL_GPL(acpi_get_hp_params_from_firmware); +/** + * acpi_get_hp_hw_control_from_firmware + * @dev: the pci_dev of the bridge that has a hotplug controller + * @flags: requested control bits for _OSC + * + * Attempt to take hotplug control from firmware. + */ +int acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev, u32 flags) +{ + acpi_status status; + acpi_handle chandle, handle = DEVICE_ACPI_HANDLE(&(dev->dev)); + struct pci_dev *pdev = dev; + struct pci_bus *parent; + struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; + + flags &= (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL | + OSC_SHPC_NATIVE_HP_CONTROL | + OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL); + if (!flags) { + err("Invalid flags %u specified!\n", flags); + return -EINVAL; + } + + /* + * Per PCI firmware specification, we should run the ACPI _OSC + * method to get control of hotplug hardware before using it. If + * an _OSC is missing, we look for an OSHP to do the same thing. + * To handle different BIOS behavior, we look for _OSC and OSHP + * within the scope of the hotplug controller and its parents, + * upto the host bridge under which this controller exists. + */ + while (!handle) { + /* + * This hotplug controller was not listed in the ACPI name + * space at all. Try to get acpi handle of parent pci bus. + */ + if (!pdev || !pdev->bus->parent) + break; + parent = pdev->bus->parent; + dbg("Could not find %s in acpi namespace, trying parent\n", + pci_name(pdev)); + if (!parent->self) + /* Parent must be a host bridge */ + handle = acpi_get_pci_rootbridge_handle( + pci_domain_nr(parent), + parent->number); + else + handle = DEVICE_ACPI_HANDLE(&(parent->self->dev)); + pdev = parent->self; + } + + while (handle) { + acpi_get_name(handle, ACPI_FULL_PATHNAME, &string); + dbg("Trying to get hotplug control for %s \n", + (char *)string.pointer); + status = pci_osc_control_set(handle, flags); + if (status == AE_NOT_FOUND) + status = acpi_run_oshp(handle); + if (ACPI_SUCCESS(status)) { + dbg("Gained control for hotplug HW for pci %s (%s)\n", + pci_name(dev), (char *)string.pointer); + kfree(string.pointer); + return 0; + } + if (acpi_root_bridge(handle)) + break; + chandle = handle; + status = acpi_get_parent(chandle, &handle); + if (ACPI_FAILURE(status)) + break; + } + + dbg("Cannot get control of hotplug hardware for pci %s\n", + pci_name(dev)); + + kfree(string.pointer); + return -ENODEV; +} +EXPORT_SYMBOL(acpi_get_hp_hw_control_from_firmware); /* acpi_root_bridge - check to see if this acpi object is a root bridge * diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 084b73efacb3..8492fab800cc 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -202,9 +202,13 @@ struct hpc_ops { #include #include -extern int pciehp_acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev); -#define pciehp_get_hp_hw_control_from_firmware(dev) \ - pciehp_acpi_get_hp_hw_control_from_firmware(dev) +static inline int pciehp_get_hp_hw_control_from_firmware(struct pci_dev *dev) +{ + u32 flags = (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL | + OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL); + return acpi_get_hp_hw_control_from_firmware(dev, flags); +} + static inline int pciehp_get_hp_params_from_firmware(struct pci_dev *dev, struct hotplug_params *hpp) { diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 7e3a3d17c334..a48021d85f22 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -1009,75 +1009,6 @@ static struct hpc_ops pciehp_hpc_ops = { .check_lnk_status = hpc_check_lnk_status, }; -#ifdef CONFIG_ACPI -int pciehp_acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev) -{ - acpi_status status; - acpi_handle chandle, handle = DEVICE_ACPI_HANDLE(&(dev->dev)); - struct pci_dev *pdev = dev; - struct pci_bus *parent; - struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; - - /* - * Per PCI firmware specification, we should run the ACPI _OSC - * method to get control of hotplug hardware before using it. - * If an _OSC is missing, we look for an OSHP to do the same thing. - * To handle different BIOS behavior, we look for _OSC and OSHP - * within the scope of the hotplug controller and its parents, upto - * the host bridge under which this controller exists. - */ - while (!handle) { - /* - * This hotplug controller was not listed in the ACPI name - * space at all. Try to get acpi handle of parent pci bus. - */ - if (!pdev || !pdev->bus->parent) - break; - parent = pdev->bus->parent; - dbg("Could not find %s in acpi namespace, trying parent\n", - pci_name(pdev)); - if (!parent->self) - /* Parent must be a host bridge */ - handle = acpi_get_pci_rootbridge_handle( - pci_domain_nr(parent), - parent->number); - else - handle = DEVICE_ACPI_HANDLE( - &(parent->self->dev)); - pdev = parent->self; - } - - while (handle) { - acpi_get_name(handle, ACPI_FULL_PATHNAME, &string); - dbg("Trying to get hotplug control for %s \n", - (char *)string.pointer); - status = pci_osc_control_set(handle, - OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL | - OSC_PCI_EXPRESS_NATIVE_HP_CONTROL); - if (status == AE_NOT_FOUND) - status = acpi_run_oshp(handle); - if (ACPI_SUCCESS(status)) { - dbg("Gained control for hotplug HW for pci %s (%s)\n", - pci_name(dev), (char *)string.pointer); - kfree(string.pointer); - return 0; - } - if (acpi_root_bridge(handle)) - break; - chandle = handle; - status = acpi_get_parent(chandle, &handle); - if (ACPI_FAILURE(status)) - break; - } - - dbg("Cannot get control of hotplug hardware for pci %s\n", - pci_name(dev)); - - kfree(string.pointer); - return -1; -} -#endif - static int pcie_init_hardware_part1(struct controller *ctrl, struct pcie_device *dev) { diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h index f66e8d6315ab..8a026f750deb 100644 --- a/drivers/pci/hotplug/shpchp.h +++ b/drivers/pci/hotplug/shpchp.h @@ -170,6 +170,7 @@ extern void shpchp_queue_pushbutton_work(struct work_struct *work); extern int shpc_init( struct controller *ctrl, struct pci_dev *pdev); #ifdef CONFIG_ACPI +#include static inline int get_hp_params_from_firmware(struct pci_dev *dev, struct hotplug_params *hpp) { @@ -177,14 +178,15 @@ static inline int get_hp_params_from_firmware(struct pci_dev *dev, return -ENODEV; return 0; } -#define get_hp_hw_control_from_firmware(pdev) \ - do { \ - if (DEVICE_ACPI_HANDLE(&(pdev->dev))) \ - acpi_run_oshp(DEVICE_ACPI_HANDLE(&(pdev->dev)));\ - } while (0) + +static inline int get_hp_hw_control_from_firmware(struct pci_dev *dev) +{ + u32 flags = OSC_SHPC_NATIVE_HP_CONTROL; + return acpi_get_hp_hw_control_from_firmware(dev, flags); +} #else #define get_hp_params_from_firmware(dev, hpp) (-ENODEV) -#define get_hp_hw_control_from_firmware(dev) do { } while (0) +#define get_hp_hw_control_from_firmware(dev) (0) #endif struct ctrl_reg { diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c index 0066b0be0928..df41ecc4e7fc 100644 --- a/drivers/pci/hotplug/shpchp_core.c +++ b/drivers/pci/hotplug/shpchp_core.c @@ -330,13 +330,14 @@ static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_sp static int is_shpc_capable(struct pci_dev *dev) { - if ((dev->vendor == PCI_VENDOR_ID_AMD) || (dev->device == - PCI_DEVICE_ID_AMD_GOLAM_7450)) - return 1; - if (pci_find_capability(dev, PCI_CAP_ID_SHPC)) - return 1; - - return 0; + if ((dev->vendor == PCI_VENDOR_ID_AMD) || (dev->device == + PCI_DEVICE_ID_AMD_GOLAM_7450)) + return 1; + if (!pci_find_capability(dev, PCI_CAP_ID_SHPC)) + return 0; + if (get_hp_hw_control_from_firmware(dev)) + return 0; + return 1; } static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) diff --git a/drivers/pci/hotplug/shpchp_hpc.c b/drivers/pci/hotplug/shpchp_hpc.c index 7d770b2cd889..7a0bff364cd4 100644 --- a/drivers/pci/hotplug/shpchp_hpc.c +++ b/drivers/pci/hotplug/shpchp_hpc.c @@ -1084,7 +1084,6 @@ int shpc_init(struct controller *ctrl, struct pci_dev *pdev) dbg("%s: HPC at b:d:f:irq=0x%x:%x:%x:%x\n", __func__, pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), pdev->irq); - get_hp_hw_control_from_firmware(pdev); /* * If this is the first controller to be initialized, diff --git a/include/linux/pci_hotplug.h b/include/linux/pci_hotplug.h index 8f67e8f2a3cc..dbdcd1ad3c6a 100644 --- a/include/linux/pci_hotplug.h +++ b/include/linux/pci_hotplug.h @@ -227,9 +227,9 @@ struct hotplug_params { #include #include #include -extern acpi_status acpi_run_oshp(acpi_handle handle); extern acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus, struct hotplug_params *hpp); +int acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev, u32 flags); int acpi_root_bridge(acpi_handle handle); #endif #endif -- cgit v1.2.3 From f46753c5e354b857b20ab8e0fe7b2579831dc369 Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Tue, 10 Jun 2008 15:28:50 -0600 Subject: PCI: introduce pci_slot Currently, /sys/bus/pci/slots/ only exposes hotplug attributes when a hotplug driver is loaded, but PCI slots have attributes such as address, speed, width, etc. that are not related to hotplug at all. Introduce pci_slot as the primary data structure and kobject model. Hotplug attributes described in hotplug_slot become a secondary structure associated with the pci_slot. This patch only creates the infrastructure that allows the separation of PCI slot attributes and hotplug attributes. In this patch, the PCI hotplug core remains the only user of this infrastructure, and thus, /sys/bus/pci/slots/ will still only become populated when a hotplug driver is loaded. A later patch in this series will add a second user of this new infrastructure and demonstrate splitting the task of exposing pci_slot attributes from hotplug_slot attributes. - Make pci_slot the primary sysfs entity. hotplug_slot becomes a subsidiary structure. o pci_create_slot() creates and registers a slot with the PCI core o pci_slot_add_hotplug() gives it hotplug capability - Change the prototype of pci_hp_register() to take the bus and slot number (on parent bus) as parameters. - Remove all the ->get_address methods since this functionality is now handled by pci_slot directly. [achiang@hp.com: rpaphp-correctly-pci_hp_register-for-empty-pci-slots] Tested-by: Badari Pulavarty Acked-by: Benjamin Herrenschmidt [akpm@linux-foundation.org: build fix] [akpm@linux-foundation.org: make headers_check happy] [akpm@linux-foundation.org: nuther build fix] [akpm@linux-foundation.org: fix typo in #include] Signed-off-by: Alex Chiang Signed-off-by: Matthew Wilcox Cc: Greg KH Cc: Kristen Carlson Accardi Cc: Len Brown Acked-by: Kenji Kaneshige Signed-off-by: Andrew Morton Signed-off-by: Jesse Barnes --- drivers/pci/Makefile | 2 +- drivers/pci/hotplug/acpiphp.h | 1 - drivers/pci/hotplug/acpiphp_core.c | 25 +--- drivers/pci/hotplug/acpiphp_glue.c | 23 +-- drivers/pci/hotplug/acpiphp_ibm.c | 6 +- drivers/pci/hotplug/cpci_hotplug_core.c | 2 +- drivers/pci/hotplug/cpqphp_core.c | 4 +- drivers/pci/hotplug/fakephp.c | 2 +- drivers/pci/hotplug/ibmphp_ebda.c | 3 +- drivers/pci/hotplug/pci_hotplug_core.c | 258 ++++++++++++-------------------- drivers/pci/hotplug/pciehp_core.c | 32 ++-- drivers/pci/hotplug/rpadlpar_sysfs.c | 5 +- drivers/pci/hotplug/rpaphp_slot.c | 44 +----- drivers/pci/hotplug/sgi_hotplug.c | 10 +- drivers/pci/hotplug/shpchp_core.c | 20 +-- drivers/pci/pci.h | 13 ++ drivers/pci/probe.c | 1 + drivers/pci/slot.c | 233 ++++++++++++++++++++++++++++ include/linux/pci.h | 19 ++- include/linux/pci_hotplug.h | 12 +- 20 files changed, 422 insertions(+), 293 deletions(-) create mode 100644 drivers/pci/slot.c (limited to 'include/linux') diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 4d1ce2e7361e..7d63f8ced24b 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -2,7 +2,7 @@ # Makefile for the PCI bus specific drivers. # -obj-y += access.o bus.o probe.o remove.o pci.o quirks.o \ +obj-y += access.o bus.o probe.o remove.o pci.o quirks.o slot.o \ pci-driver.o search.o pci-sysfs.o rom.o setup-res.o obj-$(CONFIG_PROC_FS) += proc.o diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h index 7a29164d4b32..eecf7cbf4139 100644 --- a/drivers/pci/hotplug/acpiphp.h +++ b/drivers/pci/hotplug/acpiphp.h @@ -215,7 +215,6 @@ extern u8 acpiphp_get_power_status (struct acpiphp_slot *slot); extern u8 acpiphp_get_attention_status (struct acpiphp_slot *slot); extern u8 acpiphp_get_latch_status (struct acpiphp_slot *slot); extern u8 acpiphp_get_adapter_status (struct acpiphp_slot *slot); -extern u32 acpiphp_get_address (struct acpiphp_slot *slot); /* variables */ extern int acpiphp_debug; diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c index 7af68ba27903..0e496e866a84 100644 --- a/drivers/pci/hotplug/acpiphp_core.c +++ b/drivers/pci/hotplug/acpiphp_core.c @@ -70,7 +70,6 @@ static int disable_slot (struct hotplug_slot *slot); static int set_attention_status (struct hotplug_slot *slot, u8 value); static int get_power_status (struct hotplug_slot *slot, u8 *value); static int get_attention_status (struct hotplug_slot *slot, u8 *value); -static int get_address (struct hotplug_slot *slot, u32 *value); static int get_latch_status (struct hotplug_slot *slot, u8 *value); static int get_adapter_status (struct hotplug_slot *slot, u8 *value); @@ -83,7 +82,6 @@ static struct hotplug_slot_ops acpi_hotplug_slot_ops = { .get_attention_status = get_attention_status, .get_latch_status = get_latch_status, .get_adapter_status = get_adapter_status, - .get_address = get_address, }; @@ -274,23 +272,6 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) return 0; } - -/** - * get_address - get pci address of a slot - * @hotplug_slot: slot to get status - * @value: pointer to struct pci_busdev (seg, bus, dev) - */ -static int get_address(struct hotplug_slot *hotplug_slot, u32 *value) -{ - struct slot *slot = hotplug_slot->private; - - dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name); - - *value = acpiphp_get_address(slot->acpi_slot); - - return 0; -} - static int __init init_acpi(void) { int retval; @@ -357,7 +338,11 @@ int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot) acpiphp_slot->slot = slot; snprintf(slot->name, sizeof(slot->name), "%u", slot->acpi_slot->sun); - retval = pci_hp_register(slot->hotplug_slot); + retval = pci_hp_register(slot->hotplug_slot, + acpiphp_slot->bridge->pci_bus, + acpiphp_slot->device); + if (retval == -EBUSY) + goto error_hpslot; if (retval) { err("pci_hp_register failed with error %d\n", retval); goto error_hpslot; diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 648596d469f6..9342c848db29 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -258,7 +258,12 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) bridge->pci_bus->number, slot->device); retval = acpiphp_register_hotplug_slot(slot); if (retval) { - warn("acpiphp_register_hotplug_slot failed(err code = 0x%x)\n", retval); + if (retval == -EBUSY) + warn("Slot %d already registered by another " + "hotplug driver\n", slot->sun); + else + warn("acpiphp_register_hotplug_slot failed " + "(err code = 0x%x)\n", retval); goto err_exit; } } @@ -1867,19 +1872,3 @@ u8 acpiphp_get_adapter_status(struct acpiphp_slot *slot) return (sta == 0) ? 0 : 1; } - - -/* - * pci address (seg/bus/dev) - */ -u32 acpiphp_get_address(struct acpiphp_slot *slot) -{ - u32 address; - struct pci_bus *pci_bus = slot->bridge->pci_bus; - - address = (pci_domain_nr(pci_bus) << 16) | - (pci_bus->number << 8) | - slot->device; - - return address; -} diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c index ede9051fdb5d..2b7c45e39370 100644 --- a/drivers/pci/hotplug/acpiphp_ibm.c +++ b/drivers/pci/hotplug/acpiphp_ibm.c @@ -33,8 +33,10 @@ #include #include #include +#include #include "acpiphp.h" +#include "../pci.h" #define DRIVER_VERSION "1.0.1" #define DRIVER_AUTHOR "Irene Zubarev , Vernon Mauery " @@ -430,7 +432,7 @@ static int __init ibm_acpiphp_init(void) int retval = 0; acpi_status status; struct acpi_device *device; - struct kobject *sysdir = &pci_hotplug_slots_kset->kobj; + struct kobject *sysdir = &pci_slots_kset->kobj; dbg("%s\n", __func__); @@ -477,7 +479,7 @@ init_return: static void __exit ibm_acpiphp_exit(void) { acpi_status status; - struct kobject *sysdir = &pci_hotplug_slots_kset->kobj; + struct kobject *sysdir = &pci_slots_kset->kobj; dbg("%s\n", __func__); diff --git a/drivers/pci/hotplug/cpci_hotplug_core.c b/drivers/pci/hotplug/cpci_hotplug_core.c index d8a6b80ab42a..935947991dc9 100644 --- a/drivers/pci/hotplug/cpci_hotplug_core.c +++ b/drivers/pci/hotplug/cpci_hotplug_core.c @@ -285,7 +285,7 @@ cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last) info->attention_status = cpci_get_attention_status(slot); dbg("registering slot %s", slot->hotplug_slot->name); - status = pci_hp_register(slot->hotplug_slot); + status = pci_hp_register(slot->hotplug_slot, bus, i); if (status) { err("pci_hp_register failed with error %d", status); goto error_name; diff --git a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c index 36b115b27b0b..54defec51d08 100644 --- a/drivers/pci/hotplug/cpqphp_core.c +++ b/drivers/pci/hotplug/cpqphp_core.c @@ -434,7 +434,9 @@ static int ctrl_slot_setup(struct controller *ctrl, slot->bus, slot->device, slot->number, ctrl->slot_device_offset, slot_number); - result = pci_hp_register(hotplug_slot); + result = pci_hp_register(hotplug_slot, + ctrl->pci_dev->subordinate, + slot->device); if (result) { err("pci_hp_register failed with error %d\n", result); goto error_name; diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c index b57223f7e830..40337a06c18a 100644 --- a/drivers/pci/hotplug/fakephp.c +++ b/drivers/pci/hotplug/fakephp.c @@ -126,7 +126,7 @@ static int add_slot(struct pci_dev *dev) slot->release = &dummy_release; slot->private = dslot; - retval = pci_hp_register(slot); + retval = pci_hp_register(slot, dev->bus, PCI_SLOT(dev->devfn)); if (retval) { err("pci_hp_register failed with error %d\n", retval); goto error_dslot; diff --git a/drivers/pci/hotplug/ibmphp_ebda.c b/drivers/pci/hotplug/ibmphp_ebda.c index dca7efc14be2..8467d0287325 100644 --- a/drivers/pci/hotplug/ibmphp_ebda.c +++ b/drivers/pci/hotplug/ibmphp_ebda.c @@ -1001,7 +1001,8 @@ static int __init ebda_rsrc_controller (void) tmp_slot = list_entry (list, struct slot, ibm_slot_list); snprintf (tmp_slot->hotplug_slot->name, 30, "%s", create_file_name (tmp_slot)); - pci_hp_register (tmp_slot->hotplug_slot); + pci_hp_register(tmp_slot->hotplug_slot, + pci_find_bus(0, tmp_slot->bus), tmp_slot->device); } print_ebda_hpc (); diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c index a11021e8ce37..4df31f375197 100644 --- a/drivers/pci/hotplug/pci_hotplug_core.c +++ b/drivers/pci/hotplug/pci_hotplug_core.c @@ -40,6 +40,7 @@ #include #include #include +#include "../pci.h" #define MY_NAME "pci_hotplug" @@ -60,41 +61,7 @@ static int debug; ////////////////////////////////////////////////////////////////// static LIST_HEAD(pci_hotplug_slot_list); - -struct kset *pci_hotplug_slots_kset; - -static ssize_t hotplug_slot_attr_show(struct kobject *kobj, - struct attribute *attr, char *buf) -{ - struct hotplug_slot *slot = to_hotplug_slot(kobj); - struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr); - return attribute->show ? attribute->show(slot, buf) : -EIO; -} - -static ssize_t hotplug_slot_attr_store(struct kobject *kobj, - struct attribute *attr, const char *buf, size_t len) -{ - struct hotplug_slot *slot = to_hotplug_slot(kobj); - struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr); - return attribute->store ? attribute->store(slot, buf, len) : -EIO; -} - -static struct sysfs_ops hotplug_slot_sysfs_ops = { - .show = hotplug_slot_attr_show, - .store = hotplug_slot_attr_store, -}; - -static void hotplug_slot_release(struct kobject *kobj) -{ - struct hotplug_slot *slot = to_hotplug_slot(kobj); - if (slot->release) - slot->release(slot); -} - -static struct kobj_type hotplug_slot_ktype = { - .sysfs_ops = &hotplug_slot_sysfs_ops, - .release = &hotplug_slot_release, -}; +static DEFINE_SPINLOCK(pci_hotplug_slot_list_lock); /* these strings match up with the values in pci_bus_speed */ static char *pci_bus_speed_strings[] = { @@ -149,16 +116,15 @@ GET_STATUS(power_status, u8) GET_STATUS(attention_status, u8) GET_STATUS(latch_status, u8) GET_STATUS(adapter_status, u8) -GET_STATUS(address, u32) GET_STATUS(max_bus_speed, enum pci_bus_speed) GET_STATUS(cur_bus_speed, enum pci_bus_speed) -static ssize_t power_read_file (struct hotplug_slot *slot, char *buf) +static ssize_t power_read_file(struct pci_slot *slot, char *buf) { int retval; u8 value; - retval = get_power_status (slot, &value); + retval = get_power_status(slot->hotplug, &value); if (retval) goto exit; retval = sprintf (buf, "%d\n", value); @@ -166,9 +132,10 @@ exit: return retval; } -static ssize_t power_write_file (struct hotplug_slot *slot, const char *buf, +static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf, size_t count) { + struct hotplug_slot *slot = pci_slot->hotplug; unsigned long lpower; u8 power; int retval = 0; @@ -204,29 +171,30 @@ exit: return count; } -static struct hotplug_slot_attribute hotplug_slot_attr_power = { +static struct pci_slot_attribute hotplug_slot_attr_power = { .attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR}, .show = power_read_file, .store = power_write_file }; -static ssize_t attention_read_file (struct hotplug_slot *slot, char *buf) +static ssize_t attention_read_file(struct pci_slot *slot, char *buf) { int retval; u8 value; - retval = get_attention_status (slot, &value); + retval = get_attention_status(slot->hotplug, &value); if (retval) goto exit; - retval = sprintf (buf, "%d\n", value); + retval = sprintf(buf, "%d\n", value); exit: return retval; } -static ssize_t attention_write_file (struct hotplug_slot *slot, const char *buf, +static ssize_t attention_write_file(struct pci_slot *slot, const char *buf, size_t count) { + struct hotplug_slot_ops *ops = slot->hotplug->ops; unsigned long lattention; u8 attention; int retval = 0; @@ -235,13 +203,13 @@ static ssize_t attention_write_file (struct hotplug_slot *slot, const char *buf, attention = (u8)(lattention & 0xff); dbg (" - attention = %d\n", attention); - if (!try_module_get(slot->ops->owner)) { + if (!try_module_get(ops->owner)) { retval = -ENODEV; goto exit; } - if (slot->ops->set_attention_status) - retval = slot->ops->set_attention_status(slot, attention); - module_put(slot->ops->owner); + if (ops->set_attention_status) + retval = ops->set_attention_status(slot->hotplug, attention); + module_put(ops->owner); exit: if (retval) @@ -249,18 +217,18 @@ exit: return count; } -static struct hotplug_slot_attribute hotplug_slot_attr_attention = { +static struct pci_slot_attribute hotplug_slot_attr_attention = { .attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR}, .show = attention_read_file, .store = attention_write_file }; -static ssize_t latch_read_file (struct hotplug_slot *slot, char *buf) +static ssize_t latch_read_file(struct pci_slot *slot, char *buf) { int retval; u8 value; - retval = get_latch_status (slot, &value); + retval = get_latch_status(slot->hotplug, &value); if (retval) goto exit; retval = sprintf (buf, "%d\n", value); @@ -269,17 +237,17 @@ exit: return retval; } -static struct hotplug_slot_attribute hotplug_slot_attr_latch = { +static struct pci_slot_attribute hotplug_slot_attr_latch = { .attr = {.name = "latch", .mode = S_IFREG | S_IRUGO}, .show = latch_read_file, }; -static ssize_t presence_read_file (struct hotplug_slot *slot, char *buf) +static ssize_t presence_read_file(struct pci_slot *slot, char *buf) { int retval; u8 value; - retval = get_adapter_status (slot, &value); + retval = get_adapter_status(slot->hotplug, &value); if (retval) goto exit; retval = sprintf (buf, "%d\n", value); @@ -288,42 +256,20 @@ exit: return retval; } -static struct hotplug_slot_attribute hotplug_slot_attr_presence = { +static struct pci_slot_attribute hotplug_slot_attr_presence = { .attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO}, .show = presence_read_file, }; -static ssize_t address_read_file (struct hotplug_slot *slot, char *buf) -{ - int retval; - u32 address; - - retval = get_address (slot, &address); - if (retval) - goto exit; - retval = sprintf (buf, "%04x:%02x:%02x\n", - (address >> 16) & 0xffff, - (address >> 8) & 0xff, - address & 0xff); - -exit: - return retval; -} - -static struct hotplug_slot_attribute hotplug_slot_attr_address = { - .attr = {.name = "address", .mode = S_IFREG | S_IRUGO}, - .show = address_read_file, -}; - static char *unknown_speed = "Unknown bus speed"; -static ssize_t max_bus_speed_read_file (struct hotplug_slot *slot, char *buf) +static ssize_t max_bus_speed_read_file(struct pci_slot *slot, char *buf) { char *speed_string; int retval; enum pci_bus_speed value; - retval = get_max_bus_speed (slot, &value); + retval = get_max_bus_speed(slot->hotplug, &value); if (retval) goto exit; @@ -338,18 +284,18 @@ exit: return retval; } -static struct hotplug_slot_attribute hotplug_slot_attr_max_bus_speed = { +static struct pci_slot_attribute hotplug_slot_attr_max_bus_speed = { .attr = {.name = "max_bus_speed", .mode = S_IFREG | S_IRUGO}, .show = max_bus_speed_read_file, }; -static ssize_t cur_bus_speed_read_file (struct hotplug_slot *slot, char *buf) +static ssize_t cur_bus_speed_read_file(struct pci_slot *slot, char *buf) { char *speed_string; int retval; enum pci_bus_speed value; - retval = get_cur_bus_speed (slot, &value); + retval = get_cur_bus_speed(slot->hotplug, &value); if (retval) goto exit; @@ -364,14 +310,15 @@ exit: return retval; } -static struct hotplug_slot_attribute hotplug_slot_attr_cur_bus_speed = { +static struct pci_slot_attribute hotplug_slot_attr_cur_bus_speed = { .attr = {.name = "cur_bus_speed", .mode = S_IFREG | S_IRUGO}, .show = cur_bus_speed_read_file, }; -static ssize_t test_write_file (struct hotplug_slot *slot, const char *buf, +static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf, size_t count) { + struct hotplug_slot *slot = pci_slot->hotplug; unsigned long ltest; u32 test; int retval = 0; @@ -394,13 +341,14 @@ exit: return count; } -static struct hotplug_slot_attribute hotplug_slot_attr_test = { +static struct pci_slot_attribute hotplug_slot_attr_test = { .attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR}, .store = test_write_file }; -static int has_power_file (struct hotplug_slot *slot) +static int has_power_file(struct pci_slot *pci_slot) { + struct hotplug_slot *slot = pci_slot->hotplug; if ((!slot) || (!slot->ops)) return -ENODEV; if ((slot->ops->enable_slot) || @@ -410,8 +358,9 @@ static int has_power_file (struct hotplug_slot *slot) return -ENOENT; } -static int has_attention_file (struct hotplug_slot *slot) +static int has_attention_file(struct pci_slot *pci_slot) { + struct hotplug_slot *slot = pci_slot->hotplug; if ((!slot) || (!slot->ops)) return -ENODEV; if ((slot->ops->set_attention_status) || @@ -420,8 +369,9 @@ static int has_attention_file (struct hotplug_slot *slot) return -ENOENT; } -static int has_latch_file (struct hotplug_slot *slot) +static int has_latch_file(struct pci_slot *pci_slot) { + struct hotplug_slot *slot = pci_slot->hotplug; if ((!slot) || (!slot->ops)) return -ENODEV; if (slot->ops->get_latch_status) @@ -429,8 +379,9 @@ static int has_latch_file (struct hotplug_slot *slot) return -ENOENT; } -static int has_adapter_file (struct hotplug_slot *slot) +static int has_adapter_file(struct pci_slot *pci_slot) { + struct hotplug_slot *slot = pci_slot->hotplug; if ((!slot) || (!slot->ops)) return -ENODEV; if (slot->ops->get_adapter_status) @@ -438,17 +389,9 @@ static int has_adapter_file (struct hotplug_slot *slot) return -ENOENT; } -static int has_address_file (struct hotplug_slot *slot) -{ - if ((!slot) || (!slot->ops)) - return -ENODEV; - if (slot->ops->get_address) - return 0; - return -ENOENT; -} - -static int has_max_bus_speed_file (struct hotplug_slot *slot) +static int has_max_bus_speed_file(struct pci_slot *pci_slot) { + struct hotplug_slot *slot = pci_slot->hotplug; if ((!slot) || (!slot->ops)) return -ENODEV; if (slot->ops->get_max_bus_speed) @@ -456,8 +399,9 @@ static int has_max_bus_speed_file (struct hotplug_slot *slot) return -ENOENT; } -static int has_cur_bus_speed_file (struct hotplug_slot *slot) +static int has_cur_bus_speed_file(struct pci_slot *pci_slot) { + struct hotplug_slot *slot = pci_slot->hotplug; if ((!slot) || (!slot->ops)) return -ENODEV; if (slot->ops->get_cur_bus_speed) @@ -465,8 +409,9 @@ static int has_cur_bus_speed_file (struct hotplug_slot *slot) return -ENOENT; } -static int has_test_file (struct hotplug_slot *slot) +static int has_test_file(struct pci_slot *pci_slot) { + struct hotplug_slot *slot = pci_slot->hotplug; if ((!slot) || (!slot->ops)) return -ENODEV; if (slot->ops->hardware_test) @@ -474,7 +419,7 @@ static int has_test_file (struct hotplug_slot *slot) return -ENOENT; } -static int fs_add_slot (struct hotplug_slot *slot) +static int fs_add_slot(struct pci_slot *slot) { int retval = 0; @@ -505,13 +450,6 @@ static int fs_add_slot (struct hotplug_slot *slot) goto exit_adapter; } - if (has_address_file(slot) == 0) { - retval = sysfs_create_file(&slot->kobj, - &hotplug_slot_attr_address.attr); - if (retval) - goto exit_address; - } - if (has_max_bus_speed_file(slot) == 0) { retval = sysfs_create_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr); @@ -544,10 +482,6 @@ exit_cur_speed: sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr); exit_max_speed: - if (has_address_file(slot) == 0) - sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr); - -exit_address: if (has_adapter_file(slot) == 0) sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr); @@ -567,7 +501,7 @@ exit: return retval; } -static void fs_remove_slot (struct hotplug_slot *slot) +static void fs_remove_slot(struct pci_slot *slot) { if (has_power_file(slot) == 0) sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr); @@ -581,9 +515,6 @@ static void fs_remove_slot (struct hotplug_slot *slot) if (has_adapter_file(slot) == 0) sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr); - if (has_address_file(slot) == 0) - sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr); - if (has_max_bus_speed_file(slot) == 0) sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr); @@ -599,12 +530,16 @@ static struct hotplug_slot *get_slot_from_name (const char *name) struct hotplug_slot *slot; struct list_head *tmp; + spin_lock(&pci_hotplug_slot_list_lock); list_for_each (tmp, &pci_hotplug_slot_list) { slot = list_entry (tmp, struct hotplug_slot, slot_list); if (strcmp(slot->name, name) == 0) - return slot; + goto out; } - return NULL; + slot = NULL; +out: + spin_unlock(&pci_hotplug_slot_list_lock); + return slot; } /** @@ -616,9 +551,10 @@ static struct hotplug_slot *get_slot_from_name (const char *name) * * Returns 0 if successful, anything else for an error. */ -int pci_hp_register (struct hotplug_slot *slot) +int pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus, int slot_nr) { int result; + struct pci_slot *pci_slot; struct hotplug_slot *tmp; if (slot == NULL) @@ -636,19 +572,28 @@ int pci_hp_register (struct hotplug_slot *slot) if (tmp) return -EEXIST; - slot->kobj.kset = pci_hotplug_slots_kset; - result = kobject_init_and_add(&slot->kobj, &hotplug_slot_ktype, NULL, - "%s", slot->name); - if (result) { - err("Unable to register kobject '%s'", slot->name); - return -EINVAL; + pci_slot = pci_create_slot(bus, slot_nr, slot->name); + if (IS_ERR(pci_slot)) + return PTR_ERR(pci_slot); + + if (pci_slot->hotplug) { + dbg("%s: already claimed\n", __func__); + pci_destroy_slot(pci_slot); + return -EBUSY; } - list_add (&slot->slot_list, &pci_hotplug_slot_list); + slot->pci_slot = pci_slot; + pci_slot->hotplug = slot; + + spin_lock(&pci_hotplug_slot_list_lock); + list_add(&slot->slot_list, &pci_hotplug_slot_list); + spin_unlock(&pci_hotplug_slot_list_lock); + + result = fs_add_slot(pci_slot); + kobject_uevent(&pci_slot->kobj, KOBJ_ADD); + dbg("Added slot %s to the list\n", slot->name); + - result = fs_add_slot (slot); - kobject_uevent(&slot->kobj, KOBJ_ADD); - dbg ("Added slot %s to the list\n", slot->name); return result; } @@ -661,22 +606,30 @@ int pci_hp_register (struct hotplug_slot *slot) * * Returns 0 if successful, anything else for an error. */ -int pci_hp_deregister (struct hotplug_slot *slot) +int pci_hp_deregister(struct hotplug_slot *hotplug) { struct hotplug_slot *temp; + struct pci_slot *slot; - if (slot == NULL) + if (!hotplug) return -ENODEV; - temp = get_slot_from_name (slot->name); - if (temp != slot) { + temp = get_slot_from_name(hotplug->name); + if (temp != hotplug) return -ENODEV; - } - list_del (&slot->slot_list); - fs_remove_slot (slot); - dbg ("Removed slot %s from the list\n", slot->name); - kobject_put(&slot->kobj); + spin_lock(&pci_hotplug_slot_list_lock); + list_del(&hotplug->slot_list); + spin_unlock(&pci_hotplug_slot_list_lock); + + slot = hotplug->pci_slot; + fs_remove_slot(slot); + dbg("Removed slot %s from the list\n", hotplug->name); + + hotplug->release(hotplug); + slot->hotplug = NULL; + pci_destroy_slot(slot); + return 0; } @@ -690,13 +643,15 @@ int pci_hp_deregister (struct hotplug_slot *slot) * * Returns 0 if successful, anything else for an error. */ -int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot, +int __must_check pci_hp_change_slot_info(struct hotplug_slot *hotplug, struct hotplug_slot_info *info) { - if ((slot == NULL) || (info == NULL)) + struct pci_slot *slot; + if (!hotplug || !info) return -ENODEV; + slot = hotplug->pci_slot; - memcpy (slot->info, info, sizeof (struct hotplug_slot_info)); + memcpy(hotplug->info, info, sizeof(struct hotplug_slot_info)); return 0; } @@ -704,36 +659,22 @@ int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot, static int __init pci_hotplug_init (void) { int result; - struct kset *pci_bus_kset; - pci_bus_kset = bus_get_kset(&pci_bus_type); - - pci_hotplug_slots_kset = kset_create_and_add("slots", NULL, - &pci_bus_kset->kobj); - if (!pci_hotplug_slots_kset) { - result = -ENOMEM; - err("Register subsys error\n"); - goto exit; - } result = cpci_hotplug_init(debug); if (result) { err ("cpci_hotplug_init with error %d\n", result); - goto err_subsys; + goto err_cpci; } info (DRIVER_DESC " version: " DRIVER_VERSION "\n"); - goto exit; -err_subsys: - kset_unregister(pci_hotplug_slots_kset); -exit: +err_cpci: return result; } static void __exit pci_hotplug_exit (void) { cpci_hotplug_exit(); - kset_unregister(pci_hotplug_slots_kset); } module_init(pci_hotplug_init); @@ -745,7 +686,6 @@ MODULE_LICENSE("GPL"); module_param(debug, bool, 0644); MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); -EXPORT_SYMBOL_GPL(pci_hotplug_slots_kset); EXPORT_SYMBOL_GPL(pci_hp_register); EXPORT_SYMBOL_GPL(pci_hp_deregister); EXPORT_SYMBOL_GPL(pci_hp_change_slot_info); diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 49414e9100de..7b21c86e4bff 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -72,7 +72,6 @@ static int get_power_status (struct hotplug_slot *slot, u8 *value); static int get_attention_status (struct hotplug_slot *slot, u8 *value); static int get_latch_status (struct hotplug_slot *slot, u8 *value); static int get_adapter_status (struct hotplug_slot *slot, u8 *value); -static int get_address (struct hotplug_slot *slot, u32 *value); static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); @@ -85,7 +84,6 @@ static struct hotplug_slot_ops pciehp_hotplug_slot_ops = { .get_attention_status = get_attention_status, .get_latch_status = get_latch_status, .get_adapter_status = get_adapter_status, - .get_address = get_address, .get_max_bus_speed = get_max_bus_speed, .get_cur_bus_speed = get_cur_bus_speed, }; @@ -252,7 +250,9 @@ static int init_slots(struct controller *ctrl) dbg("Registering bus=%x dev=%x hp_slot=%x sun=%x " "slot_device_offset=%x\n", slot->bus, slot->device, slot->hp_slot, slot->number, ctrl->slot_device_offset); - retval = pci_hp_register(hotplug_slot); + retval = pci_hp_register(hotplug_slot, + ctrl->pci_dev->subordinate, + slot->device); if (retval) { err("pci_hp_register failed with error %d\n", retval); if (retval == -EEXIST) @@ -263,7 +263,7 @@ static int init_slots(struct controller *ctrl) } /* create additional sysfs entries */ if (EMI(ctrl)) { - retval = sysfs_create_file(&hotplug_slot->kobj, + retval = sysfs_create_file(&hotplug_slot->pci_slot->kobj, &hotplug_slot_attr_lock.attr); if (retval) { pci_hp_deregister(hotplug_slot); @@ -296,7 +296,7 @@ static void cleanup_slots(struct controller *ctrl) slot = list_entry(tmp, struct slot, slot_list); list_del(&slot->slot_list); if (EMI(ctrl)) - sysfs_remove_file(&slot->hotplug_slot->kobj, + sysfs_remove_file(&slot->hotplug_slot->pci_slot->kobj, &hotplug_slot_attr_lock.attr); cancel_delayed_work(&slot->work); flush_scheduled_work(); @@ -398,19 +398,8 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) return 0; } -static int get_address(struct hotplug_slot *hotplug_slot, u32 *value) -{ - struct slot *slot = hotplug_slot->private; - struct pci_bus *bus = slot->ctrl->pci_dev->subordinate; - - dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name); - - *value = (pci_domain_nr(bus) << 16) | (slot->bus << 8) | slot->device; - - return 0; -} - -static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) +static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, + enum pci_bus_speed *value) { struct slot *slot = hotplug_slot->private; int retval; @@ -474,7 +463,12 @@ static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_ /* Setup the slot information structures */ rc = init_slots(ctrl); if (rc) { - err("%s: slot initialization failed\n", PCIE_MODULE_NAME); + if (rc == -EBUSY) + warn("%s: slot already registered by another " + "hotplug driver\n", PCIE_MODULE_NAME); + else + err("%s: slot initialization failed\n", + PCIE_MODULE_NAME); goto err_out_release_ctlr; } diff --git a/drivers/pci/hotplug/rpadlpar_sysfs.c b/drivers/pci/hotplug/rpadlpar_sysfs.c index 779c5db71be4..a796301ea03f 100644 --- a/drivers/pci/hotplug/rpadlpar_sysfs.c +++ b/drivers/pci/hotplug/rpadlpar_sysfs.c @@ -14,8 +14,10 @@ */ #include #include +#include #include #include "rpadlpar.h" +#include "../pci.h" #define DLPAR_KOBJ_NAME "control" @@ -27,7 +29,6 @@ #define MAX_DRC_NAME_LEN 64 - static ssize_t add_slot_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t nbytes) { @@ -112,7 +113,7 @@ int dlpar_sysfs_init(void) int error; dlpar_kobj = kobject_create_and_add(DLPAR_KOBJ_NAME, - &pci_hotplug_slots_kset->kobj); + &pci_slots_kset->kobj); if (!dlpar_kobj) return -EINVAL; diff --git a/drivers/pci/hotplug/rpaphp_slot.c b/drivers/pci/hotplug/rpaphp_slot.c index 56197b600d36..9b714ea93d20 100644 --- a/drivers/pci/hotplug/rpaphp_slot.c +++ b/drivers/pci/hotplug/rpaphp_slot.c @@ -33,33 +33,6 @@ #include #include "rpaphp.h" -static ssize_t address_read_file (struct hotplug_slot *php_slot, char *buf) -{ - int retval; - struct slot *slot = (struct slot *)php_slot->private; - struct pci_bus *bus; - - if (!slot) - return -ENOENT; - - bus = slot->bus; - if (!bus) - return -ENOENT; - - if (bus->self) - retval = sprintf(buf, pci_name(bus->self)); - else - retval = sprintf(buf, "%04x:%02x:00.0", - pci_domain_nr(bus), bus->number); - - return retval; -} - -static struct hotplug_slot_attribute php_attr_address = { - .attr = {.name = "address", .mode = S_IFREG | S_IRUGO}, - .show = address_read_file, -}; - /* free up the memory used by a slot */ static void rpaphp_release_slot(struct hotplug_slot *hotplug_slot) { @@ -135,9 +108,6 @@ int rpaphp_deregister_slot(struct slot *slot) list_del(&slot->rpaphp_slot_list); - /* remove "address" file */ - sysfs_remove_file(&php_slot->kobj, &php_attr_address.attr); - retval = pci_hp_deregister(php_slot); if (retval) err("Problem unregistering a slot %s\n", slot->name); @@ -151,6 +121,7 @@ int rpaphp_register_slot(struct slot *slot) { struct hotplug_slot *php_slot = slot->hotplug_slot; int retval; + int slotno; dbg("%s registering slot:path[%s] index[%x], name[%s] pdomain[%x] type[%d]\n", __func__, slot->dn->full_name, slot->index, slot->name, @@ -162,19 +133,16 @@ int rpaphp_register_slot(struct slot *slot) return -EAGAIN; } - retval = pci_hp_register(php_slot); + if (slot->dn->child) + slotno = PCI_SLOT(PCI_DN(slot->dn->child)->devfn); + else + slotno = -1; + retval = pci_hp_register(php_slot, slot->bus, slotno); if (retval) { err("pci_hp_register failed with error %d\n", retval); return retval; } - /* create "address" file */ - retval = sysfs_create_file(&php_slot->kobj, &php_attr_address.attr); - if (retval) { - err("sysfs_create_file failed with error %d\n", retval); - goto sysfs_fail; - } - /* add slot to our internal list */ list_add(&slot->rpaphp_slot_list, &rpaphp_slot_head); info("Slot [%s] registered\n", slot->name); diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c index 2fe37cd85b69..2036a43ff2fd 100644 --- a/drivers/pci/hotplug/sgi_hotplug.c +++ b/drivers/pci/hotplug/sgi_hotplug.c @@ -197,13 +197,15 @@ static int sn_hp_slot_private_alloc(struct hotplug_slot *bss_hotplug_slot, static struct hotplug_slot * sn_hp_destroy(void) { struct slot *slot; + struct pci_slot *pci_slot; struct hotplug_slot *bss_hotplug_slot = NULL; list_for_each_entry(slot, &sn_hp_list, hp_list) { bss_hotplug_slot = slot->hotplug_slot; + pci_slot = bss_hotplug_slot->pci_slot; list_del(&((struct slot *)bss_hotplug_slot->private)-> hp_list); - sysfs_remove_file(&bss_hotplug_slot->kobj, + sysfs_remove_file(&pci_slot->kobj, &sn_slot_path_attr.attr); break; } @@ -614,6 +616,7 @@ static void sn_release_slot(struct hotplug_slot *bss_hotplug_slot) static int sn_hotplug_slot_register(struct pci_bus *pci_bus) { int device; + struct pci_slot *pci_slot; struct hotplug_slot *bss_hotplug_slot; int rc = 0; @@ -650,11 +653,12 @@ static int sn_hotplug_slot_register(struct pci_bus *pci_bus) bss_hotplug_slot->ops = &sn_hotplug_slot_ops; bss_hotplug_slot->release = &sn_release_slot; - rc = pci_hp_register(bss_hotplug_slot); + rc = pci_hp_register(bss_hotplug_slot, pci_bus, device); if (rc) goto register_err; - rc = sysfs_create_file(&bss_hotplug_slot->kobj, + pci_slot = bss_hotplug_slot->pci_slot; + rc = sysfs_create_file(&pci_slot->kobj, &sn_slot_path_attr.attr); if (rc) goto register_err; diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c index df41ecc4e7fc..a8cbd039b85b 100644 --- a/drivers/pci/hotplug/shpchp_core.c +++ b/drivers/pci/hotplug/shpchp_core.c @@ -68,7 +68,6 @@ static int get_power_status (struct hotplug_slot *slot, u8 *value); static int get_attention_status (struct hotplug_slot *slot, u8 *value); static int get_latch_status (struct hotplug_slot *slot, u8 *value); static int get_adapter_status (struct hotplug_slot *slot, u8 *value); -static int get_address (struct hotplug_slot *slot, u32 *value); static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); @@ -81,7 +80,6 @@ static struct hotplug_slot_ops shpchp_hotplug_slot_ops = { .get_attention_status = get_attention_status, .get_latch_status = get_latch_status, .get_adapter_status = get_adapter_status, - .get_address = get_address, .get_max_bus_speed = get_max_bus_speed, .get_cur_bus_speed = get_cur_bus_speed, }; @@ -159,7 +157,8 @@ static int init_slots(struct controller *ctrl) dbg("Registering bus=%x dev=%x hp_slot=%x sun=%x " "slot_device_offset=%x\n", slot->bus, slot->device, slot->hp_slot, slot->number, ctrl->slot_device_offset); - retval = pci_hp_register(slot->hotplug_slot); + retval = pci_hp_register(slot->hotplug_slot, + ctrl->pci_dev->subordinate, slot->device); if (retval) { err("pci_hp_register failed with error %d\n", retval); if (retval == -EEXIST) @@ -288,19 +287,8 @@ static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value) return 0; } -static int get_address (struct hotplug_slot *hotplug_slot, u32 *value) -{ - struct slot *slot = get_slot(hotplug_slot); - struct pci_bus *bus = slot->ctrl->pci_dev->subordinate; - - dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name); - - *value = (pci_domain_nr(bus) << 16) | (slot->bus << 8) | slot->device; - - return 0; -} - -static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) +static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, + enum pci_bus_speed *value) { struct slot *slot = get_slot(hotplug_slot); int retval; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 0a497c1b4227..e1d7bbf079b4 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -106,3 +106,16 @@ pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev) } struct pci_dev *pci_find_upstream_pcie_bridge(struct pci_dev *pdev); + +/* PCI slot sysfs helper code */ +#define to_pci_slot(s) container_of(s, struct pci_slot, kobj) + +extern struct kset *pci_slots_kset; + +struct pci_slot_attribute { + struct attribute attr; + ssize_t (*show)(struct pci_slot *, char *); + ssize_t (*store)(struct pci_slot *, const char *, size_t); +}; +#define to_pci_slot_attr(s) container_of(s, struct pci_slot_attribute, attr) + diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index aebab71abebf..4562827b7e22 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -414,6 +414,7 @@ static struct pci_bus * pci_alloc_bus(void) INIT_LIST_HEAD(&b->node); INIT_LIST_HEAD(&b->children); INIT_LIST_HEAD(&b->devices); + INIT_LIST_HEAD(&b->slots); } return b; } diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c new file mode 100644 index 000000000000..7e5b85cbd948 --- /dev/null +++ b/drivers/pci/slot.c @@ -0,0 +1,233 @@ +/* + * drivers/pci/slot.c + * Copyright (C) 2006 Matthew Wilcox + * Copyright (C) 2006-2008 Hewlett-Packard Development Company, L.P. + * Alex Chiang + */ + +#include +#include +#include +#include "pci.h" + +struct kset *pci_slots_kset; +EXPORT_SYMBOL_GPL(pci_slots_kset); + +static ssize_t pci_slot_attr_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct pci_slot *slot = to_pci_slot(kobj); + struct pci_slot_attribute *attribute = to_pci_slot_attr(attr); + return attribute->show ? attribute->show(slot, buf) : -EIO; +} + +static ssize_t pci_slot_attr_store(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t len) +{ + struct pci_slot *slot = to_pci_slot(kobj); + struct pci_slot_attribute *attribute = to_pci_slot_attr(attr); + return attribute->store ? attribute->store(slot, buf, len) : -EIO; +} + +static struct sysfs_ops pci_slot_sysfs_ops = { + .show = pci_slot_attr_show, + .store = pci_slot_attr_store, +}; + +static ssize_t address_read_file(struct pci_slot *slot, char *buf) +{ + if (slot->number == 0xff) + return sprintf(buf, "%04x:%02x\n", + pci_domain_nr(slot->bus), + slot->bus->number); + else + return sprintf(buf, "%04x:%02x:%02x\n", + pci_domain_nr(slot->bus), + slot->bus->number, + slot->number); +} + +static void pci_slot_release(struct kobject *kobj) +{ + struct pci_slot *slot = to_pci_slot(kobj); + + pr_debug("%s: releasing pci_slot on %x:%d\n", __func__, + slot->bus->number, slot->number); + + list_del(&slot->list); + + kfree(slot); +} + +static struct pci_slot_attribute pci_slot_attr_address = + __ATTR(address, (S_IFREG | S_IRUGO), address_read_file, NULL); + +static struct attribute *pci_slot_default_attrs[] = { + &pci_slot_attr_address.attr, + NULL, +}; + +static struct kobj_type pci_slot_ktype = { + .sysfs_ops = &pci_slot_sysfs_ops, + .release = &pci_slot_release, + .default_attrs = pci_slot_default_attrs, +}; + +/** + * pci_create_slot - create or increment refcount for physical PCI slot + * @parent: struct pci_bus of parent bridge + * @slot_nr: PCI_SLOT(pci_dev->devfn) or -1 for placeholder + * @name: user visible string presented in /sys/bus/pci/slots/ + * + * PCI slots have first class attributes such as address, speed, width, + * and a &struct pci_slot is used to manage them. This interface will + * either return a new &struct pci_slot to the caller, or if the pci_slot + * already exists, its refcount will be incremented. + * + * Slots are uniquely identified by a @pci_bus, @slot_nr, @name tuple. + * + * Placeholder slots: + * In most cases, @pci_bus, @slot_nr will be sufficient to uniquely identify + * a slot. There is one notable exception - pSeries (rpaphp), where the + * @slot_nr cannot be determined until a device is actually inserted into + * the slot. In this scenario, the caller may pass -1 for @slot_nr. + * + * The following semantics are imposed when the caller passes @slot_nr == + * -1. First, the check for existing %struct pci_slot is skipped, as the + * caller may know about several unpopulated slots on a given %struct + * pci_bus, and each slot would have a @slot_nr of -1. Uniqueness for + * these slots is then determined by the @name parameter. We expect + * kobject_init_and_add() to warn us if the caller attempts to create + * multiple slots with the same name. The other change in semantics is + * user-visible, which is the 'address' parameter presented in sysfs will + * consist solely of a dddd:bb tuple, where dddd is the PCI domain of the + * %struct pci_bus and bb is the bus number. In other words, the devfn of + * the 'placeholder' slot will not be displayed. + */ + +struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr, + const char *name) +{ + struct pci_slot *slot; + int err; + + down_write(&pci_bus_sem); + + if (slot_nr == -1) + goto placeholder; + + /* If we've already created this slot, bump refcount and return. */ + list_for_each_entry(slot, &parent->slots, list) { + if (slot->number == slot_nr) { + kobject_get(&slot->kobj); + pr_debug("%s: inc refcount to %d on %04x:%02x:%02x\n", + __func__, + atomic_read(&slot->kobj.kref.refcount), + pci_domain_nr(parent), parent->number, + slot_nr); + goto out; + } + } + +placeholder: + slot = kzalloc(sizeof(*slot), GFP_KERNEL); + if (!slot) { + slot = ERR_PTR(-ENOMEM); + goto out; + } + + slot->bus = parent; + slot->number = slot_nr; + + slot->kobj.kset = pci_slots_kset; + err = kobject_init_and_add(&slot->kobj, &pci_slot_ktype, NULL, + "%s", name); + if (err) { + printk(KERN_ERR "Unable to register kobject %s\n", name); + goto err; + } + + INIT_LIST_HEAD(&slot->list); + list_add(&slot->list, &parent->slots); + + /* Don't care if debug printk has a -1 for slot_nr */ + pr_debug("%s: created pci_slot on %04x:%02x:%02x\n", + __func__, pci_domain_nr(parent), parent->number, slot_nr); + + out: + up_write(&pci_bus_sem); + return slot; + err: + kfree(slot); + slot = ERR_PTR(err); + goto out; +} +EXPORT_SYMBOL_GPL(pci_create_slot); + +/** + * pci_update_slot_number - update %struct pci_slot -> number + * @slot - %struct pci_slot to update + * @slot_nr - new number for slot + * + * The primary purpose of this interface is to allow callers who earlier + * created a placeholder slot in pci_create_slot() by passing a -1 as + * slot_nr, to update their %struct pci_slot with the correct @slot_nr. + */ + +void pci_update_slot_number(struct pci_slot *slot, int slot_nr) +{ + int name_count = 0; + struct pci_slot *tmp; + + down_write(&pci_bus_sem); + + list_for_each_entry(tmp, &slot->bus->slots, list) { + WARN_ON(tmp->number == slot_nr); + if (!strcmp(kobject_name(&tmp->kobj), kobject_name(&slot->kobj))) + name_count++; + } + + if (name_count > 1) + printk(KERN_WARNING "pci_update_slot_number found %d slots with the same name: %s\n", name_count, kobject_name(&slot->kobj)); + + slot->number = slot_nr; + up_write(&pci_bus_sem); +} +EXPORT_SYMBOL_GPL(pci_update_slot_number); + +/** + * pci_destroy_slot - decrement refcount for physical PCI slot + * @slot: struct pci_slot to decrement + * + * %struct pci_slot is refcounted, so destroying them is really easy; we + * just call kobject_put on its kobj and let our release methods do the + * rest. + */ + +void pci_destroy_slot(struct pci_slot *slot) +{ + pr_debug("%s: dec refcount to %d on %04x:%02x:%02x\n", __func__, + atomic_read(&slot->kobj.kref.refcount) - 1, + pci_domain_nr(slot->bus), slot->bus->number, slot->number); + + down_write(&pci_bus_sem); + kobject_put(&slot->kobj); + up_write(&pci_bus_sem); +} +EXPORT_SYMBOL_GPL(pci_destroy_slot); + +static int pci_slot_init(void) +{ + struct kset *pci_bus_kset; + + pci_bus_kset = bus_get_kset(&pci_bus_type); + pci_slots_kset = kset_create_and_add("slots", NULL, + &pci_bus_kset->kobj); + if (!pci_slots_kset) { + printk(KERN_ERR "PCI: Slot initialization failure\n"); + return -ENOMEM; + } + return 0; +} + +subsys_initcall(pci_slot_init); diff --git a/include/linux/pci.h b/include/linux/pci.h index 507ee52323cd..f1f73f79a180 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -17,8 +17,7 @@ #ifndef LINUX_PCI_H #define LINUX_PCI_H -/* Include the pci register defines */ -#include +#include /* The pci register defines */ /* * The PCI interface treats multi-function devices as independent @@ -49,12 +48,22 @@ #include #include #include +#include #include #include /* Include the ID list */ #include +/* pci_slot represents a physical slot */ +struct pci_slot { + struct pci_bus *bus; /* The bus this slot is on */ + struct list_head list; /* node in list of slots on this bus */ + struct hotplug_slot *hotplug; /* Hotplug info (migrate over time) */ + unsigned char number; /* PCI_SLOT(pci_dev->devfn) */ + struct kobject kobj; +}; + /* File state for mmap()s on /proc/bus/pci/X/Y */ enum pci_mmap_state { pci_mmap_io, @@ -142,6 +151,7 @@ struct pci_dev { void *sysdata; /* hook for sys-specific extension */ struct proc_dir_entry *procent; /* device entry in /proc/bus/pci */ + struct pci_slot *slot; /* Physical slot this device is in */ unsigned int devfn; /* encoded device & function index */ unsigned short vendor; @@ -266,6 +276,7 @@ struct pci_bus { struct list_head children; /* list of child buses */ struct list_head devices; /* list of devices on this bus */ struct pci_dev *self; /* bridge device as seen by parent */ + struct list_head slots; /* list of slots on this bus */ struct resource *resource[PCI_BUS_NUM_RESOURCES]; /* address space routed to this bus */ @@ -488,6 +499,10 @@ struct pci_bus *pci_create_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata); struct pci_bus *pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev, int busnr); +struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr, + const char *name); +void pci_destroy_slot(struct pci_slot *slot); +void pci_update_slot_number(struct pci_slot *slot, int slot_nr); int pci_scan_slot(struct pci_bus *bus, int devfn); struct pci_dev *pci_scan_single_device(struct pci_bus *bus, int devfn); void pci_device_add(struct pci_dev *dev, struct pci_bus *bus); diff --git a/include/linux/pci_hotplug.h b/include/linux/pci_hotplug.h index dbdcd1ad3c6a..a08cd06b541a 100644 --- a/include/linux/pci_hotplug.h +++ b/include/linux/pci_hotplug.h @@ -95,9 +95,6 @@ struct hotplug_slot_attribute { * @get_adapter_status: Called to get see if an adapter is present in the slot or not. * If this field is NULL, the value passed in the struct hotplug_slot_info * will be used when this value is requested by a user. - * @get_address: Called to get pci address of a slot. - * If this field is NULL, the value passed in the struct hotplug_slot_info - * will be used when this value is requested by a user. * @get_max_bus_speed: Called to get the max bus speed for a slot. * If this field is NULL, the value passed in the struct hotplug_slot_info * will be used when this value is requested by a user. @@ -120,7 +117,6 @@ struct hotplug_slot_ops { int (*get_attention_status) (struct hotplug_slot *slot, u8 *value); int (*get_latch_status) (struct hotplug_slot *slot, u8 *value); int (*get_adapter_status) (struct hotplug_slot *slot, u8 *value); - int (*get_address) (struct hotplug_slot *slot, u32 *value); int (*get_max_bus_speed) (struct hotplug_slot *slot, enum pci_bus_speed *value); int (*get_cur_bus_speed) (struct hotplug_slot *slot, enum pci_bus_speed *value); }; @@ -140,7 +136,6 @@ struct hotplug_slot_info { u8 attention_status; u8 latch_status; u8 adapter_status; - u32 address; enum pci_bus_speed max_bus_speed; enum pci_bus_speed cur_bus_speed; }; @@ -166,15 +161,14 @@ struct hotplug_slot { /* Variables below this are for use only by the hotplug pci core. */ struct list_head slot_list; - struct kobject kobj; + struct pci_slot *pci_slot; }; #define to_hotplug_slot(n) container_of(n, struct hotplug_slot, kobj) -extern int pci_hp_register (struct hotplug_slot *slot); -extern int pci_hp_deregister (struct hotplug_slot *slot); +extern int pci_hp_register(struct hotplug_slot *, struct pci_bus *, int nr); +extern int pci_hp_deregister(struct hotplug_slot *slot); extern int __must_check pci_hp_change_slot_info (struct hotplug_slot *slot, struct hotplug_slot_info *info); -extern struct kset *pci_hotplug_slots_kset; /* PCI Setting Record (Type 0) */ struct hpp_type0 { -- cgit v1.2.3 From 12c03f59c3909159010b87a926f5626d4380d441 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Mon, 9 Jun 2008 16:33:55 -0700 Subject: smc911x: introduce platform data flags This patch adds a new header file for platform data information together with code that adds run time bus width and irq flag support. Signed-off-by: Magnus Damm Cc: Jeff Garzik Cc: Paul Mundt Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik --- drivers/net/smc911x.c | 17 ++++++++++- drivers/net/smc911x.h | 81 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/smc911x.h | 12 ++++++++ 3 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 include/linux/smc911x.h (limited to 'include/linux') diff --git a/drivers/net/smc911x.c b/drivers/net/smc911x.c index 2ca4db85f938..fc605f276c00 100644 --- a/drivers/net/smc911x.c +++ b/drivers/net/smc911x.c @@ -1819,6 +1819,7 @@ static int __init smc911x_probe(struct net_device *dev) int i, retval; unsigned int val, chip_id, revision; const char *version_string; + unsigned long irq_flags; DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __FUNCTION__); @@ -1985,9 +1986,15 @@ static int __init smc911x_probe(struct net_device *dev) lp->ctl_rfduplx = 1; lp->ctl_rspeed = 100; +#ifdef SMC_DYNAMIC_BUS_CONFIG + irq_flags = lp->cfg.irq_flags; +#else + irq_flags = IRQF_SHARED | SMC_IRQ_SENSE; +#endif + /* Grab the IRQ */ retval = request_irq(dev->irq, &smc911x_interrupt, - IRQF_SHARED | SMC_IRQ_SENSE, dev->name, dev); + irq_flags, dev->name, dev); if (retval) goto err_out; @@ -2057,6 +2064,7 @@ err_out: */ static int smc911x_drv_probe(struct platform_device *pdev) { + struct smc91x_platdata *pd = pdev->dev.platform_data; struct net_device *ndev; struct resource *res; struct smc911x_local *lp; @@ -2090,6 +2098,13 @@ static int smc911x_drv_probe(struct platform_device *pdev) ndev->irq = platform_get_irq(pdev, 0); lp = netdev_priv(ndev); lp->netdev = ndev; +#ifdef SMC_DYNAMIC_BUS_CONFIG + if (!pd) { + ret = -EINVAL; + goto release_both; + } + memcpy(&lp->cfg, pd, sizeof(lp->cfg)); +#endif addr = ioremap(res->start, SMC911X_IO_EXTENT); if (!addr) { diff --git a/drivers/net/smc911x.h b/drivers/net/smc911x.h index 271a3e8cf683..266232271a72 100644 --- a/drivers/net/smc911x.h +++ b/drivers/net/smc911x.h @@ -29,6 +29,7 @@ #ifndef _SMC911X_H_ #define _SMC911X_H_ +#include /* * Use the DMA feature on PXA chips */ @@ -42,6 +43,12 @@ #define SMC_USE_16BIT 0 #define SMC_USE_32BIT 1 #define SMC_IRQ_SENSE IRQF_TRIGGER_LOW +#else +/* + * Default configuration + */ + +#define SMC_DYNAMIC_BUS_CONFIG #endif /* store this information for the driver.. */ @@ -92,12 +99,84 @@ struct smc911x_local { struct device *dev; #endif void __iomem *base; +#ifdef SMC_DYNAMIC_BUS_CONFIG + struct smc911x_platdata cfg; +#endif }; /* * Define the bus width specific IO macros */ +#ifdef SMC_DYNAMIC_BUS_CONFIG +static inline unsigned int SMC_inl(struct smc911x_local *lp, int reg) +{ + void __iomem *ioaddr = lp->base + reg; + + if (lp->cfg.flags & SMC911X_USE_32BIT) + return readl(ioaddr); + + if (lp->cfg.flags & SMC911X_USE_16BIT) + return readw(ioaddr) | (readw(ioaddr + 2) << 16); + + BUG(); +} + +static inline void SMC_outl(unsigned int value, struct smc911x_local *lp, + int reg) +{ + void __iomem *ioaddr = lp->base + reg; + + if (lp->cfg.flags & SMC911X_USE_32BIT) { + writel(value, ioaddr); + return; + } + + if (lp->cfg.flags & SMC911X_USE_16BIT) { + writew(value & 0xffff, ioaddr); + writew(value >> 16, ioaddr + 2); + return; + } + + BUG(); +} + +static inline void SMC_insl(struct smc911x_local *lp, int reg, + void *addr, unsigned int count) +{ + void __iomem *ioaddr = lp->base + reg; + + if (lp->cfg.flags & SMC911X_USE_32BIT) { + readsl(ioaddr, addr, count); + return; + } + + if (lp->cfg.flags & SMC911X_USE_16BIT) { + readsw(ioaddr, addr, count * 2); + return; + } + + BUG(); +} + +static inline void SMC_outsl(struct smc911x_local *lp, int reg, + void *addr, unsigned int count) +{ + void __iomem *ioaddr = lp->base + reg; + + if (lp->cfg.flags & SMC911X_USE_32BIT) { + writesl(ioaddr, addr, count); + return; + } + + if (lp->cfg.flags & SMC911X_USE_16BIT) { + writesw(ioaddr, addr, count * 2); + return; + } + + BUG(); +} +#else #if SMC_USE_16BIT #define SMC_inl(lp, r) ((readw((lp)->base + (r)) & 0xFFFF) + (readw((lp)->base + (r) + 2) << 16)) #define SMC_outl(v, lp, r) \ @@ -115,6 +194,8 @@ struct smc911x_local { #define SMC_outsl(lp, r, p, l) writesl((int*)((lp)->base + (r)), p, l) #endif /* SMC_USE_16BIT */ +#endif /* SMC_DYNAMIC_BUS_CONFIG */ + #ifdef SMC_USE_PXA_DMA #define SMC_USE_DMA diff --git a/include/linux/smc911x.h b/include/linux/smc911x.h new file mode 100644 index 000000000000..b58f54c24183 --- /dev/null +++ b/include/linux/smc911x.h @@ -0,0 +1,12 @@ +#ifndef __SMC911X_H__ +#define __SMC911X_H__ + +#define SMC911X_USE_16BIT (1 << 0) +#define SMC911X_USE_32BIT (1 << 1) + +struct smc911x_platdata { + unsigned long flags; + unsigned long irq_flags; /* IRQF_... */ +}; + +#endif /* __SMC911X_H__ */ -- cgit v1.2.3 From 0b040829952d84bf2a62526f0e24b624e0699447 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 10 Jun 2008 22:46:50 -0700 Subject: net: remove CVS keywords This patch removes CVS keywords that weren't updated for a long time from comments. Signed-off-by: Adrian Bunk Signed-off-by: David S. Miller --- include/linux/if_bridge.h | 2 -- include/linux/if_ppp.h | 2 -- include/linux/if_tun.h | 2 -- include/linux/ip6_tunnel.h | 4 ---- include/linux/ppp-comp.h | 2 -- include/linux/ppp_defs.h | 2 -- include/linux/sunrpc/auth_gss.h | 2 -- include/linux/sunrpc/gss_api.h | 2 -- include/linux/sunrpc/svcauth_gss.h | 3 --- include/net/inetpeer.h | 2 -- include/net/ip6_tunnel.h | 4 ---- include/net/ipconfig.h | 2 -- include/net/ipv6.h | 2 -- include/net/snmp.h | 2 -- net/bluetooth/bnep/bnep.h | 4 ---- net/bluetooth/bnep/core.c | 4 ---- net/bluetooth/bnep/netdev.c | 4 ---- net/bluetooth/bnep/sock.c | 4 ---- net/bluetooth/rfcomm/core.c | 2 -- net/bluetooth/rfcomm/sock.c | 2 -- net/bluetooth/rfcomm/tty.c | 2 -- net/bridge/br.c | 2 -- net/bridge/br_device.c | 2 -- net/bridge/br_fdb.c | 2 -- net/bridge/br_forward.c | 2 -- net/bridge/br_if.c | 2 -- net/bridge/br_input.c | 2 -- net/bridge/br_ioctl.c | 2 -- net/bridge/br_notify.c | 2 -- net/bridge/br_private.h | 2 -- net/bridge/br_private_stp.h | 2 -- net/bridge/br_stp.c | 2 -- net/bridge/br_stp_bpdu.c | 2 -- net/bridge/br_stp_if.c | 2 -- net/bridge/br_stp_timer.c | 2 -- net/core/skbuff.c | 2 -- net/core/sock.c | 2 -- net/ipv4/af_inet.c | 2 -- net/ipv4/arp.c | 2 -- net/ipv4/devinet.c | 2 -- net/ipv4/fib_frontend.c | 2 -- net/ipv4/fib_hash.c | 2 -- net/ipv4/fib_semantics.c | 2 -- net/ipv4/fib_trie.c | 2 -- net/ipv4/icmp.c | 2 -- net/ipv4/igmp.c | 2 -- net/ipv4/inet_diag.c | 2 -- net/ipv4/inetpeer.c | 2 -- net/ipv4/ip_forward.c | 2 -- net/ipv4/ip_fragment.c | 2 -- net/ipv4/ip_input.c | 2 -- net/ipv4/ip_options.c | 2 -- net/ipv4/ip_output.c | 2 -- net/ipv4/ip_sockglue.c | 2 -- net/ipv4/ipconfig.c | 2 -- net/ipv4/ipip.c | 2 -- net/ipv4/ipmr.c | 2 -- net/ipv4/ipvs/ip_vs_app.c | 2 -- net/ipv4/ipvs/ip_vs_conn.c | 2 -- net/ipv4/ipvs/ip_vs_core.c | 2 -- net/ipv4/ipvs/ip_vs_ctl.c | 2 -- net/ipv4/ipvs/ip_vs_dh.c | 2 -- net/ipv4/ipvs/ip_vs_est.c | 2 -- net/ipv4/ipvs/ip_vs_ftp.c | 2 -- net/ipv4/ipvs/ip_vs_lblc.c | 2 -- net/ipv4/ipvs/ip_vs_lblcr.c | 2 -- net/ipv4/ipvs/ip_vs_lc.c | 2 -- net/ipv4/ipvs/ip_vs_nq.c | 2 -- net/ipv4/ipvs/ip_vs_proto.c | 2 -- net/ipv4/ipvs/ip_vs_proto_ah.c | 2 -- net/ipv4/ipvs/ip_vs_proto_esp.c | 2 -- net/ipv4/ipvs/ip_vs_proto_tcp.c | 2 -- net/ipv4/ipvs/ip_vs_proto_udp.c | 2 -- net/ipv4/ipvs/ip_vs_rr.c | 2 -- net/ipv4/ipvs/ip_vs_sched.c | 2 -- net/ipv4/ipvs/ip_vs_sed.c | 2 -- net/ipv4/ipvs/ip_vs_sh.c | 2 -- net/ipv4/ipvs/ip_vs_sync.c | 2 -- net/ipv4/ipvs/ip_vs_wlc.c | 2 -- net/ipv4/ipvs/ip_vs_wrr.c | 2 -- net/ipv4/ipvs/ip_vs_xmit.c | 2 -- net/ipv4/proc.c | 2 -- net/ipv4/protocol.c | 2 -- net/ipv4/raw.c | 2 -- net/ipv4/route.c | 2 -- net/ipv4/syncookies.c | 2 -- net/ipv4/sysctl_net_ipv4.c | 2 -- net/ipv4/tcp.c | 2 -- net/ipv4/tcp_diag.c | 2 -- net/ipv4/tcp_input.c | 2 -- net/ipv4/tcp_ipv4.c | 2 -- net/ipv4/tcp_minisocks.c | 2 -- net/ipv4/tcp_output.c | 2 -- net/ipv4/tcp_timer.c | 2 -- net/ipv4/udp.c | 2 -- net/ipv4/udplite.c | 2 -- net/ipv6/addrconf.c | 2 -- net/ipv6/af_inet6.c | 2 -- net/ipv6/datagram.c | 2 -- net/ipv6/exthdrs.c | 2 -- net/ipv6/icmp.c | 2 -- net/ipv6/ip6_fib.c | 2 -- net/ipv6/ip6_input.c | 2 -- net/ipv6/ip6_output.c | 2 -- net/ipv6/ip6_tunnel.c | 2 -- net/ipv6/ipv6_sockglue.c | 2 -- net/ipv6/mcast.c | 2 -- net/ipv6/proc.c | 2 -- net/ipv6/protocol.c | 2 -- net/ipv6/raw.c | 2 -- net/ipv6/reassembly.c | 2 -- net/ipv6/route.c | 2 -- net/ipv6/sit.c | 2 -- net/ipv6/tcp_ipv6.c | 2 -- net/ipv6/udp.c | 2 -- net/ipv6/udplite.c | 2 -- net/packet/af_packet.c | 2 -- net/sched/sch_htb.c | 2 -- net/sunrpc/auth_gss/auth_gss.c | 2 -- net/sysctl_net.c | 1 - net/unix/af_unix.c | 2 -- 121 files changed, 254 deletions(-) (limited to 'include/linux') diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h index 950e13d09e06..6badb3e2c4e4 100644 --- a/include/linux/if_bridge.h +++ b/include/linux/if_bridge.h @@ -4,8 +4,6 @@ * Authors: * Lennert Buytenhek * - * $Id: if_bridge.h,v 1.1 2000/02/18 16:47:01 davem Exp $ - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version diff --git a/include/linux/if_ppp.h b/include/linux/if_ppp.h index 0f2f70d4e48c..c3b1f8562709 100644 --- a/include/linux/if_ppp.h +++ b/include/linux/if_ppp.h @@ -1,5 +1,3 @@ -/* $Id: if_ppp.h,v 1.21 2000/03/27 06:03:36 paulus Exp $ */ - /* * if_ppp.h - Point-to-Point Protocol definitions. * diff --git a/include/linux/if_tun.h b/include/linux/if_tun.h index 8c71fe2fb1f5..18f31b6187a3 100644 --- a/include/linux/if_tun.h +++ b/include/linux/if_tun.h @@ -11,8 +11,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * $Id: if_tun.h,v 1.2 2001/06/01 18:39:47 davem Exp $ */ #ifndef __IF_TUN_H diff --git a/include/linux/ip6_tunnel.h b/include/linux/ip6_tunnel.h index af3f4a70f3df..1e7cc4af40de 100644 --- a/include/linux/ip6_tunnel.h +++ b/include/linux/ip6_tunnel.h @@ -1,7 +1,3 @@ -/* - * $Id$ - */ - #ifndef _IP6_TUNNEL_H #define _IP6_TUNNEL_H diff --git a/include/linux/ppp-comp.h b/include/linux/ppp-comp.h index e86a7a5cf355..b8d4ddd22736 100644 --- a/include/linux/ppp-comp.h +++ b/include/linux/ppp-comp.h @@ -23,8 +23,6 @@ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, * OR MODIFICATIONS. - * - * $Id: ppp-comp.h,v 1.6 1997/11/27 06:04:44 paulus Exp $ */ /* diff --git a/include/linux/ppp_defs.h b/include/linux/ppp_defs.h index c6b13ff85028..6e8adc77522c 100644 --- a/include/linux/ppp_defs.h +++ b/include/linux/ppp_defs.h @@ -1,5 +1,3 @@ -/* $Id: ppp_defs.h,v 1.2 1994/09/21 01:31:06 paulus Exp $ */ - /* * ppp_defs.h - PPP definitions. * diff --git a/include/linux/sunrpc/auth_gss.h b/include/linux/sunrpc/auth_gss.h index fec6899bf355..d48d4e605f74 100644 --- a/include/linux/sunrpc/auth_gss.h +++ b/include/linux/sunrpc/auth_gss.h @@ -7,8 +7,6 @@ * Andy Adamson * Bruce Fields * Copyright (c) 2000 The Regents of the University of Michigan - * - * $Id$ */ #ifndef _LINUX_SUNRPC_AUTH_GSS_H diff --git a/include/linux/sunrpc/gss_api.h b/include/linux/sunrpc/gss_api.h index 459c5fc11d51..03f33330ece2 100644 --- a/include/linux/sunrpc/gss_api.h +++ b/include/linux/sunrpc/gss_api.h @@ -7,8 +7,6 @@ * Andy Adamson * Bruce Fields * Copyright (c) 2000 The Regents of the University of Michigan - * - * $Id$ */ #ifndef _LINUX_SUNRPC_GSS_API_H diff --git a/include/linux/sunrpc/svcauth_gss.h b/include/linux/sunrpc/svcauth_gss.h index 417a1def56db..c9165d9771a8 100644 --- a/include/linux/sunrpc/svcauth_gss.h +++ b/include/linux/sunrpc/svcauth_gss.h @@ -3,9 +3,6 @@ * * Bruce Fields * Copyright (c) 2002 The Regents of the Unviersity of Michigan - * - * $Id$ - * */ #ifndef _LINUX_SUNRPC_SVCAUTH_GSS_H diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h index ad8404b56113..15e1f8fe4c1f 100644 --- a/include/net/inetpeer.h +++ b/include/net/inetpeer.h @@ -1,8 +1,6 @@ /* * INETPEER - A storage for permanent information about peers * - * Version: $Id: inetpeer.h,v 1.2 2002/01/12 07:54:56 davem Exp $ - * * Authors: Andrey V. Savochkin */ diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h index 3780592ebe8e..83b4e008b16d 100644 --- a/include/net/ip6_tunnel.h +++ b/include/net/ip6_tunnel.h @@ -1,7 +1,3 @@ -/* - * $Id$ - */ - #ifndef _NET_IP6_TUNNEL_H #define _NET_IP6_TUNNEL_H diff --git a/include/net/ipconfig.h b/include/net/ipconfig.h index 3924d7d2cb11..c74cc1bd5a02 100644 --- a/include/net/ipconfig.h +++ b/include/net/ipconfig.h @@ -1,6 +1,4 @@ /* - * $Id: ipconfig.h,v 1.4 2001/04/30 04:51:46 davem Exp $ - * * Copyright (C) 1997 Martin Mares * * Automatic IP Layer Configuration diff --git a/include/net/ipv6.h b/include/net/ipv6.h index e0a612bc9c4e..7f7db8d57934 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -4,8 +4,6 @@ * Authors: * Pedro Roque * - * $Id: ipv6.h,v 1.1 2002/05/20 15:13:07 jgrimm Exp $ - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version diff --git a/include/net/snmp.h b/include/net/snmp.h index ce2f48507510..57c93628695f 100644 --- a/include/net/snmp.h +++ b/include/net/snmp.h @@ -14,8 +14,6 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * $Id: snmp.h,v 1.19 2001/06/14 13:40:46 davem Exp $ - * */ #ifndef _SNMP_H diff --git a/net/bluetooth/bnep/bnep.h b/net/bluetooth/bnep/bnep.h index e69244dd8de8..b69bf4e7c48b 100644 --- a/net/bluetooth/bnep/bnep.h +++ b/net/bluetooth/bnep/bnep.h @@ -16,10 +16,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* - * $Id: bnep.h,v 1.5 2002/08/04 21:23:58 maxk Exp $ - */ - #ifndef _BNEP_H #define _BNEP_H diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index f85d94643aaf..1d98a1b80da7 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -25,10 +25,6 @@ SOFTWARE IS DISCLAIMED. */ -/* - * $Id: core.c,v 1.20 2002/08/04 21:23:58 maxk Exp $ - */ - #include #include diff --git a/net/bluetooth/bnep/netdev.c b/net/bluetooth/bnep/netdev.c index 95e3837e4312..d9fa0ab2c87f 100644 --- a/net/bluetooth/bnep/netdev.c +++ b/net/bluetooth/bnep/netdev.c @@ -25,10 +25,6 @@ SOFTWARE IS DISCLAIMED. */ -/* - * $Id: netdev.c,v 1.8 2002/08/04 21:23:58 maxk Exp $ - */ - #include #include diff --git a/net/bluetooth/bnep/sock.c b/net/bluetooth/bnep/sock.c index 201e5b1ce473..8ffb57f2303a 100644 --- a/net/bluetooth/bnep/sock.c +++ b/net/bluetooth/bnep/sock.c @@ -24,10 +24,6 @@ SOFTWARE IS DISCLAIMED. */ -/* - * $Id: sock.c,v 1.4 2002/08/04 21:23:58 maxk Exp $ - */ - #include #include diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 0c2c93735e93..b4fb84e398e5 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -23,8 +23,6 @@ /* * Bluetooth RFCOMM core. - * - * $Id: core.c,v 1.42 2002/10/01 23:26:25 maxk Exp $ */ #include diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index 5083adcbfae5..c9054487670a 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -23,8 +23,6 @@ /* * RFCOMM sockets. - * - * $Id: sock.c,v 1.24 2002/10/03 01:00:34 maxk Exp $ */ #include diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index c9191871c1e0..be84f4fc1477 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -23,8 +23,6 @@ /* * RFCOMM TTY. - * - * $Id: tty.c,v 1.24 2002/10/03 01:54:38 holtmann Exp $ */ #include diff --git a/net/bridge/br.c b/net/bridge/br.c index 8f3c58e5f7a5..cede010f4ddd 100644 --- a/net/bridge/br.c +++ b/net/bridge/br.c @@ -5,8 +5,6 @@ * Authors: * Lennert Buytenhek * - * $Id: br.c,v 1.47 2001/12/24 00:56:41 davem Exp $ - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 626c7795ae30..a6ffc6c2a69f 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -5,8 +5,6 @@ * Authors: * Lennert Buytenhek * - * $Id: br_device.c,v 1.6 2001/12/24 00:59:55 davem Exp $ - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index 72c5976a5ce3..4de74cdd091d 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -5,8 +5,6 @@ * Authors: * Lennert Buytenhek * - * $Id: br_fdb.c,v 1.6 2002/01/17 00:57:07 davem Exp $ - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index a4711674b3df..512645727f51 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -5,8 +5,6 @@ * Authors: * Lennert Buytenhek * - * $Id: br_forward.c,v 1.4 2001/08/14 22:05:57 davem Exp $ - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index c2397f503b0f..143c954681b8 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -5,8 +5,6 @@ * Authors: * Lennert Buytenhek * - * $Id: br_if.c,v 1.7 2001/12/24 00:59:55 davem Exp $ - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index fa0f5711a996..0145e9416714 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -5,8 +5,6 @@ * Authors: * Lennert Buytenhek * - * $Id: br_input.c,v 1.10 2001/12/24 04:50:20 davem Exp $ - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version diff --git a/net/bridge/br_ioctl.c b/net/bridge/br_ioctl.c index 0655a5f07f58..eeee218eed80 100644 --- a/net/bridge/br_ioctl.c +++ b/net/bridge/br_ioctl.c @@ -5,8 +5,6 @@ * Authors: * Lennert Buytenhek * - * $Id: br_ioctl.c,v 1.4 2000/11/08 05:16:40 davem Exp $ - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version diff --git a/net/bridge/br_notify.c b/net/bridge/br_notify.c index 00644a544e3c..88d8ec7b3142 100644 --- a/net/bridge/br_notify.c +++ b/net/bridge/br_notify.c @@ -5,8 +5,6 @@ * Authors: * Lennert Buytenhek * - * $Id: br_notify.c,v 1.2 2000/02/21 15:51:34 davem Exp $ - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 0243cb489edd..83ff5861c2d2 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -4,8 +4,6 @@ * Authors: * Lennert Buytenhek * - * $Id: br_private.h,v 1.7 2001/12/24 00:59:55 davem Exp $ - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version diff --git a/net/bridge/br_private_stp.h b/net/bridge/br_private_stp.h index e29f01ac1adf..8b650f7fbfa0 100644 --- a/net/bridge/br_private_stp.h +++ b/net/bridge/br_private_stp.h @@ -4,8 +4,6 @@ * Authors: * Lennert Buytenhek * - * $Id: br_private_stp.h,v 1.3 2001/02/05 06:03:47 davem Exp $ - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index e38034aa56f5..284d1b2fa1ff 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -5,8 +5,6 @@ * Authors: * Lennert Buytenhek * - * $Id: br_stp.c,v 1.4 2000/06/19 10:13:35 davem Exp $ - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c index ddeb6e5d45d6..9dc2de656965 100644 --- a/net/bridge/br_stp_bpdu.c +++ b/net/bridge/br_stp_bpdu.c @@ -5,8 +5,6 @@ * Authors: * Lennert Buytenhek * - * $Id: br_stp_bpdu.c,v 1.3 2001/11/10 02:35:25 davem Exp $ - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c index 1a430eccec9b..1a4e5c37a0cf 100644 --- a/net/bridge/br_stp_if.c +++ b/net/bridge/br_stp_if.c @@ -5,8 +5,6 @@ * Authors: * Lennert Buytenhek * - * $Id: br_stp_if.c,v 1.4 2001/04/14 21:14:39 davem Exp $ - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version diff --git a/net/bridge/br_stp_timer.c b/net/bridge/br_stp_timer.c index 77f5255e6915..772a140bfdf0 100644 --- a/net/bridge/br_stp_timer.c +++ b/net/bridge/br_stp_timer.c @@ -5,8 +5,6 @@ * Authors: * Lennert Buytenhek * - * $Id: br_stp_timer.c,v 1.3 2000/05/05 02:17:17 davem Exp $ - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 1e556d312117..3e18f8525e82 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -4,8 +4,6 @@ * Authors: Alan Cox * Florian La Roche * - * Version: $Id: skbuff.c,v 1.90 2001/11/07 05:56:19 davem Exp $ - * * Fixes: * Alan Cox : Fixed the worst of the load * balancer bugs. diff --git a/net/core/sock.c b/net/core/sock.c index 88094cb09c06..3879bf65897e 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -7,8 +7,6 @@ * handler for protocols to use and generic option handler. * * - * Version: $Id: sock.c,v 1.117 2002/02/01 22:01:03 davem Exp $ - * * Authors: Ross Biro * Fred N. van Kempen, * Florian La Roche, diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 24eca23c2db3..42bd24b64b57 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -5,8 +5,6 @@ * * PF_INET protocol family socket handler. * - * Version: $Id: af_inet.c,v 1.137 2002/02/01 22:01:03 davem Exp $ - * * Authors: Ross Biro * Fred N. van Kempen, * Florian La Roche, diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 9b539fa9fe18..20c515a1be28 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -1,6 +1,4 @@ /* linux/net/ipv4/arp.c - * - * Version: $Id: arp.c,v 1.99 2001/08/30 22:55:42 davem Exp $ * * Copyright (C) 1994 by Florian La Roche * diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 61011e1d580e..f8c0b0aea93a 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -1,8 +1,6 @@ /* * NET3 IP device support routines. * - * Version: $Id: devinet.c,v 1.44 2001/10/31 21:55:54 davem Exp $ - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 0b2ac6a3d903..5ad01d63f83b 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -5,8 +5,6 @@ * * IPv4 Forwarding Information Base: FIB frontend. * - * Version: $Id: fib_frontend.c,v 1.26 2001/10/31 21:55:54 davem Exp $ - * * Authors: Alexey Kuznetsov, * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/fib_hash.c b/net/ipv4/fib_hash.c index 2e2fc3376ac9..eeec4bf982b8 100644 --- a/net/ipv4/fib_hash.c +++ b/net/ipv4/fib_hash.c @@ -5,8 +5,6 @@ * * IPv4 FIB: lookup engine and maintenance routines. * - * Version: $Id: fib_hash.c,v 1.13 2001/10/31 21:55:54 davem Exp $ - * * Authors: Alexey Kuznetsov, * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 3b83c34019fc..9335eba683c3 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -5,8 +5,6 @@ * * IPv4 Forwarding Information Base: semantics. * - * Version: $Id: fib_semantics.c,v 1.19 2002/01/12 07:54:56 davem Exp $ - * * Authors: Alexey Kuznetsov, * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 4b02d14e7ab9..394db9c941a1 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -22,8 +22,6 @@ * IP-address lookup using LC-tries. Stefan Nilsson and Gunnar Karlsson * IEEE Journal on Selected Areas in Communications, 17(6):1083-1092, June 1999 * - * Version: $Id: fib_trie.c,v 1.3 2005/06/08 14:20:01 robert Exp $ - * * * Code from fib_hash has been reused which includes the following header: * diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 87397351ddac..aa7cf46853b7 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -3,8 +3,6 @@ * * Alan Cox, * - * Version: $Id: icmp.c,v 1.85 2002/02/01 22:01:03 davem Exp $ - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 2769dc4a4c84..68e84a933e90 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -8,8 +8,6 @@ * the older version didn't come out right using gcc 2.5.8, the newer one * seems to fall out with gcc 2.6.2. * - * Version: $Id: igmp.c,v 1.47 2002/02/01 22:01:03 davem Exp $ - * * Authors: * Alan Cox * diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index da97695e7096..c10036e7a463 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -1,8 +1,6 @@ /* * inet_diag.c Module for monitoring INET transport protocols sockets. * - * Version: $Id: inet_diag.c,v 1.3 2002/02/01 22:01:04 davem Exp $ - * * Authors: Alexey Kuznetsov, * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index af995198f643..a456ceeac3f2 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -3,8 +3,6 @@ * * This source is covered by the GNU GPL, the same as all kernel sources. * - * Version: $Id: inetpeer.c,v 1.7 2001/09/20 21:22:50 davem Exp $ - * * Authors: Andrey V. Savochkin */ diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c index 4813c39b438b..37d36a3f33cd 100644 --- a/net/ipv4/ip_forward.c +++ b/net/ipv4/ip_forward.c @@ -5,8 +5,6 @@ * * The IP forwarding functionality. * - * Version: $Id: ip_forward.c,v 1.48 2000/12/13 18:31:48 davem Exp $ - * * Authors: see ip.c * * Fixes: diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index be1cb89a8d5a..91e321407313 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -5,8 +5,6 @@ * * The IP fragmentation functionality. * - * Version: $Id: ip_fragment.c,v 1.59 2002/01/12 07:54:56 davem Exp $ - * * Authors: Fred N. van Kempen * Alan Cox * diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index ff77a4a7f9ec..7c26428ea67b 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -5,8 +5,6 @@ * * The Internet Protocol (IP) module. * - * Version: $Id: ip_input.c,v 1.55 2002/01/12 07:39:45 davem Exp $ - * * Authors: Ross Biro * Fred N. van Kempen, * Donald Becker, diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c index 33126ad2cfdc..be3f18a7a40e 100644 --- a/net/ipv4/ip_options.c +++ b/net/ipv4/ip_options.c @@ -5,8 +5,6 @@ * * The options processing module for ip.c * - * Version: $Id: ip_options.c,v 1.21 2001/09/01 00:31:50 davem Exp $ - * * Authors: A.N.Kuznetsov * */ diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index e527628f56cf..f1278eecf56d 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -5,8 +5,6 @@ * * The Internet Protocol (IP) output module. * - * Version: $Id: ip_output.c,v 1.100 2002/02/01 22:01:03 davem Exp $ - * * Authors: Ross Biro * Fred N. van Kempen, * Donald Becker, diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index e0514e82308e..105d92a039b9 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -5,8 +5,6 @@ * * The IP to API glue. * - * Version: $Id: ip_sockglue.c,v 1.62 2002/02/01 22:01:04 davem Exp $ - * * Authors: see ip.c * * Fixes: diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c index ed45037ce9be..b88aa9afa42e 100644 --- a/net/ipv4/ipconfig.c +++ b/net/ipv4/ipconfig.c @@ -1,6 +1,4 @@ /* - * $Id: ipconfig.c,v 1.46 2002/02/01 22:01:04 davem Exp $ - * * Automatic Configuration of IP -- use DHCP, BOOTP, RARP, or * user-supplied information to configure own IP address and routes. * diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 86d8836551b9..4c6d2caf9203 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -1,8 +1,6 @@ /* * Linux NET3: IP/IP protocol decoder. * - * Version: $Id: ipip.c,v 1.50 2001/10/02 02:22:36 davem Exp $ - * * Authors: * Sam Lantinga (slouken@cs.ucdavis.edu) 02/01/95 * diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index a34da4977c73..300ab0c2919e 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -9,8 +9,6 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Version: $Id: ipmr.c,v 1.65 2001/10/31 21:55:54 davem Exp $ - * * Fixes: * Michael Chastain : Incorrect size of copying. * Alan Cox : Added the cache manager code diff --git a/net/ipv4/ipvs/ip_vs_app.c b/net/ipv4/ipvs/ip_vs_app.c index 535abe0c45e7..1f1897a1a702 100644 --- a/net/ipv4/ipvs/ip_vs_app.c +++ b/net/ipv4/ipvs/ip_vs_app.c @@ -1,8 +1,6 @@ /* * ip_vs_app.c: Application module support for IPVS * - * Version: $Id: ip_vs_app.c,v 1.17 2003/03/22 06:31:21 wensong Exp $ - * * Authors: Wensong Zhang * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/ipvs/ip_vs_conn.c b/net/ipv4/ipvs/ip_vs_conn.c index 65f1ba112752..f8bdae47a77f 100644 --- a/net/ipv4/ipvs/ip_vs_conn.c +++ b/net/ipv4/ipvs/ip_vs_conn.c @@ -5,8 +5,6 @@ * high-performance and highly available server based on a * cluster of servers. * - * Version: $Id: ip_vs_conn.c,v 1.31 2003/04/18 09:03:16 wensong Exp $ - * * Authors: Wensong Zhang * Peter Kese * Julian Anastasov diff --git a/net/ipv4/ipvs/ip_vs_core.c b/net/ipv4/ipvs/ip_vs_core.c index 963981a9d501..bcf6276ba4b2 100644 --- a/net/ipv4/ipvs/ip_vs_core.c +++ b/net/ipv4/ipvs/ip_vs_core.c @@ -5,8 +5,6 @@ * high-performance and highly available server based on a * cluster of servers. * - * Version: $Id: ip_vs_core.c,v 1.34 2003/05/10 03:05:23 wensong Exp $ - * * Authors: Wensong Zhang * Peter Kese * Julian Anastasov diff --git a/net/ipv4/ipvs/ip_vs_ctl.c b/net/ipv4/ipvs/ip_vs_ctl.c index 94c5767c8e01..9a5ace0b4dd6 100644 --- a/net/ipv4/ipvs/ip_vs_ctl.c +++ b/net/ipv4/ipvs/ip_vs_ctl.c @@ -5,8 +5,6 @@ * high-performance and highly available server based on a * cluster of servers. * - * Version: $Id: ip_vs_ctl.c,v 1.36 2003/06/08 09:31:19 wensong Exp $ - * * Authors: Wensong Zhang * Peter Kese * Julian Anastasov diff --git a/net/ipv4/ipvs/ip_vs_dh.c b/net/ipv4/ipvs/ip_vs_dh.c index dcf5d46aaa5e..8afc1503ed20 100644 --- a/net/ipv4/ipvs/ip_vs_dh.c +++ b/net/ipv4/ipvs/ip_vs_dh.c @@ -1,8 +1,6 @@ /* * IPVS: Destination Hashing scheduling module * - * Version: $Id: ip_vs_dh.c,v 1.5 2002/09/15 08:14:08 wensong Exp $ - * * Authors: Wensong Zhang * * Inspired by the consistent hashing scheduler patch from diff --git a/net/ipv4/ipvs/ip_vs_est.c b/net/ipv4/ipvs/ip_vs_est.c index dfa0d713c801..bc04eedd6dbb 100644 --- a/net/ipv4/ipvs/ip_vs_est.c +++ b/net/ipv4/ipvs/ip_vs_est.c @@ -1,8 +1,6 @@ /* * ip_vs_est.c: simple rate estimator for IPVS * - * Version: $Id: ip_vs_est.c,v 1.4 2002/11/30 01:50:35 wensong Exp $ - * * Authors: Wensong Zhang * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/ipvs/ip_vs_ftp.c b/net/ipv4/ipvs/ip_vs_ftp.c index 59aa166b7678..c1c758e4f733 100644 --- a/net/ipv4/ipvs/ip_vs_ftp.c +++ b/net/ipv4/ipvs/ip_vs_ftp.c @@ -1,8 +1,6 @@ /* * ip_vs_ftp.c: IPVS ftp application module * - * Version: $Id: ip_vs_ftp.c,v 1.13 2002/09/15 08:14:08 wensong Exp $ - * * Authors: Wensong Zhang * * Changes: diff --git a/net/ipv4/ipvs/ip_vs_lblc.c b/net/ipv4/ipvs/ip_vs_lblc.c index 3888642706ad..0efa3db4b180 100644 --- a/net/ipv4/ipvs/ip_vs_lblc.c +++ b/net/ipv4/ipvs/ip_vs_lblc.c @@ -1,8 +1,6 @@ /* * IPVS: Locality-Based Least-Connection scheduling module * - * Version: $Id: ip_vs_lblc.c,v 1.10 2002/09/15 08:14:08 wensong Exp $ - * * Authors: Wensong Zhang * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/ipvs/ip_vs_lblcr.c b/net/ipv4/ipvs/ip_vs_lblcr.c index daa260eb21cf..8e3bbeb45138 100644 --- a/net/ipv4/ipvs/ip_vs_lblcr.c +++ b/net/ipv4/ipvs/ip_vs_lblcr.c @@ -1,8 +1,6 @@ /* * IPVS: Locality-Based Least-Connection with Replication scheduler * - * Version: $Id: ip_vs_lblcr.c,v 1.11 2002/09/15 08:14:08 wensong Exp $ - * * Authors: Wensong Zhang * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/ipvs/ip_vs_lc.c b/net/ipv4/ipvs/ip_vs_lc.c index d88fef90a641..ac9f08e065d5 100644 --- a/net/ipv4/ipvs/ip_vs_lc.c +++ b/net/ipv4/ipvs/ip_vs_lc.c @@ -1,8 +1,6 @@ /* * IPVS: Least-Connection Scheduling module * - * Version: $Id: ip_vs_lc.c,v 1.10 2003/04/18 09:03:16 wensong Exp $ - * * Authors: Wensong Zhang * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/ipvs/ip_vs_nq.c b/net/ipv4/ipvs/ip_vs_nq.c index bc2a9e5f2a7b..a46bf258d420 100644 --- a/net/ipv4/ipvs/ip_vs_nq.c +++ b/net/ipv4/ipvs/ip_vs_nq.c @@ -1,8 +1,6 @@ /* * IPVS: Never Queue scheduling module * - * Version: $Id: ip_vs_nq.c,v 1.2 2003/06/08 09:31:19 wensong Exp $ - * * Authors: Wensong Zhang * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/ipvs/ip_vs_proto.c b/net/ipv4/ipvs/ip_vs_proto.c index 4b1c16cbb16b..876714f23d65 100644 --- a/net/ipv4/ipvs/ip_vs_proto.c +++ b/net/ipv4/ipvs/ip_vs_proto.c @@ -1,8 +1,6 @@ /* * ip_vs_proto.c: transport protocol load balancing support for IPVS * - * Version: $Id: ip_vs_proto.c,v 1.2 2003/04/18 09:03:16 wensong Exp $ - * * Authors: Wensong Zhang * Julian Anastasov * diff --git a/net/ipv4/ipvs/ip_vs_proto_ah.c b/net/ipv4/ipvs/ip_vs_proto_ah.c index 4bf835e1d86d..73e0ea87c1f5 100644 --- a/net/ipv4/ipvs/ip_vs_proto_ah.c +++ b/net/ipv4/ipvs/ip_vs_proto_ah.c @@ -1,8 +1,6 @@ /* * ip_vs_proto_ah.c: AH IPSec load balancing support for IPVS * - * Version: $Id: ip_vs_proto_ah.c,v 1.1 2003/07/04 15:04:37 wensong Exp $ - * * Authors: Julian Anastasov , February 2002 * Wensong Zhang * diff --git a/net/ipv4/ipvs/ip_vs_proto_esp.c b/net/ipv4/ipvs/ip_vs_proto_esp.c index db6a6b7b1a0b..21d70c8ffa54 100644 --- a/net/ipv4/ipvs/ip_vs_proto_esp.c +++ b/net/ipv4/ipvs/ip_vs_proto_esp.c @@ -1,8 +1,6 @@ /* * ip_vs_proto_esp.c: ESP IPSec load balancing support for IPVS * - * Version: $Id: ip_vs_proto_esp.c,v 1.1 2003/07/04 15:04:37 wensong Exp $ - * * Authors: Julian Anastasov , February 2002 * Wensong Zhang * diff --git a/net/ipv4/ipvs/ip_vs_proto_tcp.c b/net/ipv4/ipvs/ip_vs_proto_tcp.c index b83dc14b0a4d..d0ea467986a0 100644 --- a/net/ipv4/ipvs/ip_vs_proto_tcp.c +++ b/net/ipv4/ipvs/ip_vs_proto_tcp.c @@ -1,8 +1,6 @@ /* * ip_vs_proto_tcp.c: TCP load balancing support for IPVS * - * Version: $Id: ip_vs_proto_tcp.c,v 1.3 2002/11/30 01:50:35 wensong Exp $ - * * Authors: Wensong Zhang * Julian Anastasov * diff --git a/net/ipv4/ipvs/ip_vs_proto_udp.c b/net/ipv4/ipvs/ip_vs_proto_udp.c index 75771cb3cd6f..c6be5d56823f 100644 --- a/net/ipv4/ipvs/ip_vs_proto_udp.c +++ b/net/ipv4/ipvs/ip_vs_proto_udp.c @@ -1,8 +1,6 @@ /* * ip_vs_proto_udp.c: UDP load balancing support for IPVS * - * Version: $Id: ip_vs_proto_udp.c,v 1.3 2002/11/30 01:50:35 wensong Exp $ - * * Authors: Wensong Zhang * Julian Anastasov * diff --git a/net/ipv4/ipvs/ip_vs_rr.c b/net/ipv4/ipvs/ip_vs_rr.c index 433f8a947924..c8db12d39e61 100644 --- a/net/ipv4/ipvs/ip_vs_rr.c +++ b/net/ipv4/ipvs/ip_vs_rr.c @@ -1,8 +1,6 @@ /* * IPVS: Round-Robin Scheduling module * - * Version: $Id: ip_vs_rr.c,v 1.9 2002/09/15 08:14:08 wensong Exp $ - * * Authors: Wensong Zhang * Peter Kese * diff --git a/net/ipv4/ipvs/ip_vs_sched.c b/net/ipv4/ipvs/ip_vs_sched.c index 121a32b1b756..b64767309855 100644 --- a/net/ipv4/ipvs/ip_vs_sched.c +++ b/net/ipv4/ipvs/ip_vs_sched.c @@ -5,8 +5,6 @@ * high-performance and highly available server based on a * cluster of servers. * - * Version: $Id: ip_vs_sched.c,v 1.13 2003/05/10 03:05:23 wensong Exp $ - * * Authors: Wensong Zhang * Peter Kese * diff --git a/net/ipv4/ipvs/ip_vs_sed.c b/net/ipv4/ipvs/ip_vs_sed.c index dd7c128f9db3..2a7d31358181 100644 --- a/net/ipv4/ipvs/ip_vs_sed.c +++ b/net/ipv4/ipvs/ip_vs_sed.c @@ -1,8 +1,6 @@ /* * IPVS: Shortest Expected Delay scheduling module * - * Version: $Id: ip_vs_sed.c,v 1.1 2003/05/10 03:06:08 wensong Exp $ - * * Authors: Wensong Zhang * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/ipvs/ip_vs_sh.c b/net/ipv4/ipvs/ip_vs_sh.c index 1b25b00ef1e1..b8fdfac65001 100644 --- a/net/ipv4/ipvs/ip_vs_sh.c +++ b/net/ipv4/ipvs/ip_vs_sh.c @@ -1,8 +1,6 @@ /* * IPVS: Source Hashing scheduling module * - * Version: $Id: ip_vs_sh.c,v 1.5 2002/09/15 08:14:08 wensong Exp $ - * * Authors: Wensong Zhang * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/ipvs/ip_vs_sync.c b/net/ipv4/ipvs/ip_vs_sync.c index eff54efe0351..2d4a86f73325 100644 --- a/net/ipv4/ipvs/ip_vs_sync.c +++ b/net/ipv4/ipvs/ip_vs_sync.c @@ -5,8 +5,6 @@ * high-performance and highly available server based on a * cluster of servers. * - * Version: $Id: ip_vs_sync.c,v 1.13 2003/06/08 09:31:19 wensong Exp $ - * * Authors: Wensong Zhang * * ip_vs_sync: sync connection info from master load balancer to backups diff --git a/net/ipv4/ipvs/ip_vs_wlc.c b/net/ipv4/ipvs/ip_vs_wlc.c index 8a9d913261d8..772c3cb4eca1 100644 --- a/net/ipv4/ipvs/ip_vs_wlc.c +++ b/net/ipv4/ipvs/ip_vs_wlc.c @@ -1,8 +1,6 @@ /* * IPVS: Weighted Least-Connection Scheduling module * - * Version: $Id: ip_vs_wlc.c,v 1.13 2003/04/18 09:03:16 wensong Exp $ - * * Authors: Wensong Zhang * Peter Kese * diff --git a/net/ipv4/ipvs/ip_vs_wrr.c b/net/ipv4/ipvs/ip_vs_wrr.c index 85c680add6df..1d6932d7dc97 100644 --- a/net/ipv4/ipvs/ip_vs_wrr.c +++ b/net/ipv4/ipvs/ip_vs_wrr.c @@ -1,8 +1,6 @@ /* * IPVS: Weighted Round-Robin Scheduling module * - * Version: $Id: ip_vs_wrr.c,v 1.12 2002/09/15 08:14:08 wensong Exp $ - * * Authors: Wensong Zhang * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/ipvs/ip_vs_xmit.c b/net/ipv4/ipvs/ip_vs_xmit.c index f63006caea03..9892d4aca42e 100644 --- a/net/ipv4/ipvs/ip_vs_xmit.c +++ b/net/ipv4/ipvs/ip_vs_xmit.c @@ -1,8 +1,6 @@ /* * ip_vs_xmit.c: various packet transmitters for IPVS * - * Version: $Id: ip_vs_xmit.c,v 1.2 2002/11/30 01:50:35 wensong Exp $ - * * Authors: Wensong Zhang * Julian Anastasov * diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 552169b41b16..eb5cee279c5f 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -7,8 +7,6 @@ * PROC file system. It is mainly used for debugging and * statistics. * - * Version: $Id: proc.c,v 1.45 2001/05/16 16:45:35 davem Exp $ - * * Authors: Fred N. van Kempen, * Gerald J. Heim, * Fred Baumgarten, diff --git a/net/ipv4/protocol.c b/net/ipv4/protocol.c index 971ab9356e51..ea50da0649fd 100644 --- a/net/ipv4/protocol.c +++ b/net/ipv4/protocol.c @@ -5,8 +5,6 @@ * * INET protocol dispatch tables. * - * Version: $Id: protocol.c,v 1.14 2001/05/18 02:25:49 davem Exp $ - * * Authors: Ross Biro * Fred N. van Kempen, * diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index e7e091d365ff..1d0c97c8712d 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -5,8 +5,6 @@ * * RAW - implementation of IP "raw" sockets. * - * Version: $Id: raw.c,v 1.64 2002/02/01 22:01:04 davem Exp $ - * * Authors: Ross Biro * Fred N. van Kempen, * diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 96be336064fb..fe3a02237286 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -5,8 +5,6 @@ * * ROUTE - implementation of the IP router. * - * Version: $Id: route.c,v 1.103 2002/01/12 07:44:09 davem Exp $ - * * Authors: Ross Biro * Fred N. van Kempen, * Alan Cox, diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 73ba98921d64..6317d3c8dc0d 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -8,8 +8,6 @@ * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. - * - * $Id: syncookies.c,v 1.18 2002/02/01 22:01:04 davem Exp $ */ #include diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index c437f804ee38..901607003205 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -1,8 +1,6 @@ /* * sysctl_net_ipv4.c: sysctl interface to net IPV4 subsystem. * - * $Id: sysctl_net_ipv4.c,v 1.50 2001/10/20 00:00:11 davem Exp $ - * * Begun April 1, 1996, Mike Shaver. * Added /proc/sys/net/ipv4 directory entry (empty =) ). [MS] */ diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index ab66683b8043..ad66b09e0bcd 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -5,8 +5,6 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp.c,v 1.216 2002/02/01 22:01:04 davem Exp $ - * * Authors: Ross Biro * Fred N. van Kempen, * Mark Evans, diff --git a/net/ipv4/tcp_diag.c b/net/ipv4/tcp_diag.c index 2fbcc7d1b1a0..838d491dfda7 100644 --- a/net/ipv4/tcp_diag.c +++ b/net/ipv4/tcp_diag.c @@ -1,8 +1,6 @@ /* * tcp_diag.c Module for monitoring TCP transport protocols sockets. * - * Version: $Id: tcp_diag.c,v 1.3 2002/02/01 22:01:04 davem Exp $ - * * Authors: Alexey Kuznetsov, * * This program is free software; you can redistribute it and/or diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index eba873e9b560..b68c3c7d906b 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5,8 +5,6 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_input.c,v 1.243 2002/02/01 22:01:04 davem Exp $ - * * Authors: Ross Biro * Fred N. van Kempen, * Mark Evans, diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index cd601a866c2f..f2926ae1de57 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -5,8 +5,6 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_ipv4.c,v 1.240 2002/02/01 22:01:04 davem Exp $ - * * IPv4 specific functions * * diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 019c8c16e5cc..1276cab85e3e 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -5,8 +5,6 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_minisocks.c,v 1.15 2002/02/01 22:01:04 davem Exp $ - * * Authors: Ross Biro * Fred N. van Kempen, * Mark Evans, diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index ad993ecb4810..b171ac65ccab 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -5,8 +5,6 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_output.c,v 1.146 2002/02/01 22:01:04 davem Exp $ - * * Authors: Ross Biro * Fred N. van Kempen, * Mark Evans, diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index 4de68cf5f2aa..e77e7ae0bf2c 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -5,8 +5,6 @@ * * Implementation of the Transmission Control Protocol(TCP). * - * Version: $Id: tcp_timer.c,v 1.88 2002/02/01 22:01:04 davem Exp $ - * * Authors: Ross Biro * Fred N. van Kempen, * Mark Evans, diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 56fcda3694ba..355e6d62d483 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -5,8 +5,6 @@ * * The User Datagram Protocol (UDP). * - * Version: $Id: udp.c,v 1.102 2002/02/01 22:01:04 davem Exp $ - * * Authors: Ross Biro * Fred N. van Kempen, * Arnt Gulbrandsen, diff --git a/net/ipv4/udplite.c b/net/ipv4/udplite.c index 72ce26b6c4d3..4ad16b6d5138 100644 --- a/net/ipv4/udplite.c +++ b/net/ipv4/udplite.c @@ -1,8 +1,6 @@ /* * UDPLITE An implementation of the UDP-Lite protocol (RFC 3828). * - * Version: $Id: udplite.c,v 1.25 2006/10/19 07:22:36 gerrit Exp $ - * * Authors: Gerrit Renker * * Changes: diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 147588f4c7c0..deb38bf03376 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -6,8 +6,6 @@ * Pedro Roque * Alexey Kuznetsov * - * $Id: addrconf.c,v 1.69 2001/10/31 21:55:54 davem Exp $ - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index e84b3fd17fb4..350457c761e6 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -7,8 +7,6 @@ * * Adapted from linux/net/ipv4/af_inet.c * - * $Id: af_inet6.c,v 1.66 2002/02/01 22:01:04 davem Exp $ - * * Fixes: * piggy, Karl Knutson : Socket protocol table * Hideaki YOSHIFUJI : sin6_scope_id support diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index b9c2de84a8a2..8cdb6b65ee96 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -5,8 +5,6 @@ * Authors: * Pedro Roque * - * $Id: datagram.c,v 1.24 2002/02/01 22:01:04 davem Exp $ - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 3cd1c993d52b..602ea826f0a5 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -7,8 +7,6 @@ * Andi Kleen * Alexey Kuznetsov * - * $Id: exthdrs.c,v 1.13 2001/06/19 15:58:56 davem Exp $ - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index d42dd16d3487..399d41f65437 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -5,8 +5,6 @@ * Authors: * Pedro Roque * - * $Id: icmp.c,v 1.38 2002/02/08 03:57:19 davem Exp $ - * * Based on net/ipv4/icmp.c * * RFC 1885 diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 1ee4fa17c129..4de2b9efcacb 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -5,8 +5,6 @@ * Authors: * Pedro Roque * - * $Id: ip6_fib.c,v 1.25 2001/10/31 21:55:55 davem Exp $ - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index 4e5c8615832c..f77a6011c302 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -6,8 +6,6 @@ * Pedro Roque * Ian P. Morris * - * $Id: ip6_input.c,v 1.19 2000/12/13 18:31:50 davem Exp $ - * * Based in linux/net/ipv4/ip_input.c * * This program is free software; you can redistribute it and/or diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 48cdce9c696c..40a2813a63d1 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -5,8 +5,6 @@ * Authors: * Pedro Roque * - * $Id: ip6_output.c,v 1.34 2002/02/01 22:01:04 davem Exp $ - * * Based on linux/net/ipv4/ip_output.c * * This program is free software; you can redistribute it and/or diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 37814810ac49..17c7b098cdb0 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -6,8 +6,6 @@ * Ville Nuorvala * Yasuyuki Kozakai * - * $Id$ - * * Based on: * linux/net/ipv6/sit.c and linux/net/ipv4/ipip.c * diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 26b83e512a09..237ebbb9383b 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -7,8 +7,6 @@ * * Based on linux/net/ipv4/ip_sockglue.c * - * $Id: ipv6_sockglue.c,v 1.41 2002/02/01 22:01:04 davem Exp $ - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index fd632dd7f98d..fbb2d12c41bc 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -5,8 +5,6 @@ * Authors: * Pedro Roque * - * $Id: mcast.c,v 1.40 2002/02/08 03:57:19 davem Exp $ - * * Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c * * This program is free software; you can redistribute it and/or diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c index df0736a4cafa..cbc7e514d3ec 100644 --- a/net/ipv6/proc.c +++ b/net/ipv6/proc.c @@ -7,8 +7,6 @@ * PROC file system. This is very similar to the IPv4 version, * except it reports the sockets in the INET6 address family. * - * Version: $Id: proc.c,v 1.17 2002/02/01 22:01:04 davem Exp $ - * * Authors: David S. Miller (davem@caip.rutgers.edu) * YOSHIFUJI Hideaki * diff --git a/net/ipv6/protocol.c b/net/ipv6/protocol.c index f929f47b925e..9ab789159913 100644 --- a/net/ipv6/protocol.c +++ b/net/ipv6/protocol.c @@ -5,8 +5,6 @@ * * PF_INET6 protocol dispatch tables. * - * Version: $Id: protocol.c,v 1.10 2001/05/18 02:25:49 davem Exp $ - * * Authors: Pedro Roque * * This program is free software; you can redistribute it and/or diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 8fee9a15b2d3..e03c1898ab2e 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -7,8 +7,6 @@ * * Adapted from linux/net/ipv4/raw.c * - * $Id: raw.c,v 1.51 2002/02/01 22:01:04 davem Exp $ - * * Fixes: * Hideaki YOSHIFUJI : sin6_scope_id support * YOSHIFUJI,H.@USAGI : raw checksum (RFC2292(bis) compliance) diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 9391a6949b96..13509f906d89 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -5,8 +5,6 @@ * Authors: * Pedro Roque * - * $Id: reassembly.c,v 1.26 2001/03/07 22:00:57 davem Exp $ - * * Based on: net/ipv4/ip_fragment.c * * This program is free software; you can redistribute it and/or diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 220cffe9e63b..edae81319b51 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -5,8 +5,6 @@ * Authors: * Pedro Roque * - * $Id: route.c,v 1.56 2001/10/31 21:55:55 davem Exp $ - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 6b8f0583b637..b0c5080420a8 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -6,8 +6,6 @@ * Pedro Roque * Alexey Kuznetsov * - * $Id: sit.c,v 1.53 2001/09/25 05:09:53 davem Exp $ - * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 715965f0fac0..155499197fc5 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -5,8 +5,6 @@ * Authors: * Pedro Roque * - * $Id: tcp_ipv6.c,v 1.144 2002/02/01 22:01:04 davem Exp $ - * * Based on: * linux/net/ipv4/tcp.c * linux/net/ipv4/tcp_input.c diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index dd309626ae9a..e0693fffc9bd 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -7,8 +7,6 @@ * * Based on linux/ipv4/udp.c * - * $Id: udp.c,v 1.65 2002/02/01 22:01:04 davem Exp $ - * * Fixes: * Hideaki YOSHIFUJI : sin6_scope_id support * YOSHIFUJI Hideaki @USAGI and: Support IPV6_V6ONLY socket option, which diff --git a/net/ipv6/udplite.c b/net/ipv6/udplite.c index 491efd00a866..f6cdcb348e05 100644 --- a/net/ipv6/udplite.c +++ b/net/ipv6/udplite.c @@ -2,8 +2,6 @@ * UDPLITEv6 An implementation of the UDP-Lite protocol over IPv6. * See also net/ipv4/udplite.c * - * Version: $Id: udplite.c,v 1.9 2006/10/19 08:28:10 gerrit Exp $ - * * Authors: Gerrit Renker * * Changes: diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 2cee87da4441..beca6402f1cf 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -5,8 +5,6 @@ * * PACKET - implements raw packet sockets. * - * Version: $Id: af_packet.c,v 1.61 2002/02/08 03:57:19 davem Exp $ - * * Authors: Ross Biro * Fred N. van Kempen, * Alan Cox, diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 5bc1ed490180..213071859030 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -24,8 +24,6 @@ * Jiri Fojtasek * fixed requeue routine * and many others. thanks. - * - * $Id: sch_htb.c,v 1.25 2003/12/07 11:08:25 devik Exp devik $ */ #include #include diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index cc12d5f5d5da..019d4b4478c9 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -33,8 +33,6 @@ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $Id$ */ diff --git a/net/sysctl_net.c b/net/sysctl_net.c index d8e79162724c..007c1a6708ee 100644 --- a/net/sysctl_net.c +++ b/net/sysctl_net.c @@ -4,7 +4,6 @@ * Begun April 1, 1996, Mike Shaver. * Added /proc/sys/net directories for each protocol family. [MS] * - * $Log: sysctl_net.c,v $ * Revision 1.2 1996/05/08 20:24:40 shaver * Added bits for NET_BRIDGE and the NET_IPV4_ARP stuff and * NET_IPV4_IP_FORWARD. diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index e18cd3628db4..392e80e3268d 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -8,8 +8,6 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Version: $Id: af_unix.c,v 1.133 2002/02/08 03:57:19 davem Exp $ - * * Fixes: * Linus Torvalds : Assorted bug cures. * Niibe Yutaka : async I/O support. -- cgit v1.2.3 From fc32b0e28df6655a15b488aaddfc1339f82dc13a Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 2 Jun 2008 00:28:40 +0200 Subject: mv643xx_eth: general cleanup General cleanup of the mv643xx_eth driver. Mainly fixes coding style / indentation issues, get rid of some useless 'volatile's, kill some more superfluous comments, and such. Signed-off-by: Lennert Buytenhek Acked-by: Dale Farnsworth --- drivers/net/mv643xx_eth.c | 949 ++++++++++++++++++++------------------------ include/linux/mv643xx_eth.h | 59 ++- 2 files changed, 479 insertions(+), 529 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index ff6460124307..cf18419f96ab 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -67,8 +67,6 @@ static char mv643xx_eth_driver_version[] = "1.0"; #define MAX_DESCS_PER_SKB 1 #endif -#define ETH_HW_IP_ALIGN 2 - /* * Registers shared between all ports. */ @@ -158,12 +156,6 @@ static char mv643xx_eth_driver_version[] = "1.0"; #define DEFAULT_RX_QUEUE_SIZE 400 #define DEFAULT_TX_QUEUE_SIZE 800 -/* SMI reg */ -#define SMI_BUSY 0x10000000 /* 0 - Write, 1 - Read */ -#define SMI_READ_VALID 0x08000000 /* 0 - Write, 1 - Read */ -#define SMI_OPCODE_WRITE 0 /* Completion of Read */ -#define SMI_OPCODE_READ 0x04000000 /* Operation is in progress */ - /* * RX/TX descriptors. @@ -231,13 +223,24 @@ struct tx_desc { /* global *******************************************************************/ struct mv643xx_eth_shared_private { + /* + * Ethernet controller base address. + */ void __iomem *base; - /* used to protect SMI_REG, which is shared across ports */ + /* + * Protects access to SMI_REG, which is shared between ports. + */ spinlock_t phy_lock; + /* + * Per-port MBUS window access register value. + */ u32 win_protect; + /* + * Hardware-specific parameters. + */ unsigned int t_clk; }; @@ -306,16 +309,17 @@ struct tx_queue { struct mv643xx_eth_private { struct mv643xx_eth_shared_private *shared; - int port_num; /* User Ethernet port number */ + int port_num; - struct mv643xx_eth_shared_private *shared_smi; + struct net_device *dev; - struct work_struct tx_timeout_task; + struct mv643xx_eth_shared_private *shared_smi; + int phy_addr; - struct net_device *dev; - struct mib_counters mib_counters; spinlock_t lock; + struct mib_counters mib_counters; + struct work_struct tx_timeout_task; struct mii_if_info mii; /* @@ -450,7 +454,12 @@ static void rxq_refill(struct rx_queue *rxq) RX_ENABLE_INTERRUPT; wmb(); - skb_reserve(skb, ETH_HW_IP_ALIGN); + /* + * The hardware automatically prepends 2 bytes of + * dummy data to each received packet, so that the + * IP header ends up 16-byte aligned. + */ + skb_reserve(skb, 2); } if (rxq->rx_desc_count == 0) { @@ -474,9 +483,9 @@ static int rxq_process(struct rx_queue *rxq, int budget) rx = 0; while (rx < budget) { - struct sk_buff *skb; - volatile struct rx_desc *rx_desc; + struct rx_desc *rx_desc; unsigned int cmd_sts; + struct sk_buff *skb; unsigned long flags; spin_lock_irqsave(&mp->lock, flags); @@ -497,34 +506,40 @@ static int rxq_process(struct rx_queue *rxq, int budget) spin_unlock_irqrestore(&mp->lock, flags); - dma_unmap_single(NULL, rx_desc->buf_ptr + ETH_HW_IP_ALIGN, - mp->dev->mtu + 24, DMA_FROM_DEVICE); + dma_unmap_single(NULL, rx_desc->buf_ptr + 2, + mp->dev->mtu + 24, DMA_FROM_DEVICE); rxq->rx_desc_count--; rx++; /* * Update statistics. - * Note byte count includes 4 byte CRC count + * + * Note that the descriptor byte count includes 2 dummy + * bytes automatically inserted by the hardware at the + * start of the packet (which we don't count), and a 4 + * byte CRC at the end of the packet (which we do count). */ stats->rx_packets++; - stats->rx_bytes += rx_desc->byte_cnt - ETH_HW_IP_ALIGN; + stats->rx_bytes += rx_desc->byte_cnt - 2; /* - * In case received a packet without first / last bits on OR - * the error summary bit is on, the packets needs to be dropeed. + * In case we received a packet without first / last bits + * on, or the error summary bit is set, the packet needs + * to be dropped. */ if (((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) != (RX_FIRST_DESC | RX_LAST_DESC)) || (cmd_sts & ERROR_SUMMARY)) { stats->rx_dropped++; + if ((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) != (RX_FIRST_DESC | RX_LAST_DESC)) { if (net_ratelimit()) - printk(KERN_ERR - "%s: Received packet spread " - "on multiple descriptors\n", - mp->dev->name); + dev_printk(KERN_ERR, &mp->dev->dev, + "received packet spanning " + "multiple descriptors\n"); } + if (cmd_sts & ERROR_SUMMARY) stats->rx_errors++; @@ -534,7 +549,7 @@ static int rxq_process(struct rx_queue *rxq, int budget) * The -4 is for the CRC in the trailer of the * received packet */ - skb_put(skb, rx_desc->byte_cnt - ETH_HW_IP_ALIGN - 4); + skb_put(skb, rx_desc->byte_cnt - 2 - 4); if (cmd_sts & LAYER_4_CHECKSUM_OK) { skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -548,8 +563,10 @@ static int rxq_process(struct rx_queue *rxq, int budget) netif_rx(skb); #endif } + mp->dev->last_rx = jiffies; } + rxq_refill(rxq); return rx; @@ -716,7 +733,7 @@ static void txq_submit_skb(struct tx_queue *txq, struct sk_buff *skb) txq->tx_desc_count += nr_frags + 1; } -static int mv643xx_eth_start_xmit(struct sk_buff *skb, struct net_device *dev) +static int mv643xx_eth_xmit(struct sk_buff *skb, struct net_device *dev) { struct mv643xx_eth_private *mp = netdev_priv(dev); struct net_device_stats *stats = &dev->stats; @@ -727,8 +744,9 @@ static int mv643xx_eth_start_xmit(struct sk_buff *skb, struct net_device *dev) if (has_tiny_unaligned_frags(skb) && __skb_linearize(skb)) { stats->tx_dropped++; - printk(KERN_DEBUG "%s: failed to linearize tiny " - "unaligned fragment\n", dev->name); + dev_printk(KERN_DEBUG, &dev->dev, + "failed to linearize skb with tiny " + "unaligned fragment\n"); return NETDEV_TX_BUSY; } @@ -758,13 +776,15 @@ static int mv643xx_eth_start_xmit(struct sk_buff *skb, struct net_device *dev) /* mii management interface *************************************************/ -static int phy_addr_get(struct mv643xx_eth_private *mp); +#define SMI_BUSY 0x10000000 +#define SMI_READ_VALID 0x08000000 +#define SMI_OPCODE_READ 0x04000000 +#define SMI_OPCODE_WRITE 0x00000000 -static void read_smi_reg(struct mv643xx_eth_private *mp, - unsigned int phy_reg, unsigned int *value) +static void smi_reg_read(struct mv643xx_eth_private *mp, unsigned int addr, + unsigned int reg, unsigned int *value) { void __iomem *smi_reg = mp->shared_smi->base + SMI_REG; - int phy_addr = phy_addr_get(mp); unsigned long flags; int i; @@ -780,7 +800,7 @@ static void read_smi_reg(struct mv643xx_eth_private *mp, udelay(10); } - writel((phy_addr << 16) | (phy_reg << 21) | SMI_OPCODE_READ, smi_reg); + writel(SMI_OPCODE_READ | (reg << 21) | (addr << 16), smi_reg); /* now wait for the data to be valid */ for (i = 0; !(readl(smi_reg) & SMI_READ_VALID); i++) { @@ -796,11 +816,11 @@ out: spin_unlock_irqrestore(&mp->shared_smi->phy_lock, flags); } -static void write_smi_reg(struct mv643xx_eth_private *mp, - unsigned int phy_reg, unsigned int value) +static void smi_reg_write(struct mv643xx_eth_private *mp, + unsigned int addr, + unsigned int reg, unsigned int value) { void __iomem *smi_reg = mp->shared_smi->base + SMI_REG; - int phy_addr = phy_addr_get(mp); unsigned long flags; int i; @@ -816,65 +836,63 @@ static void write_smi_reg(struct mv643xx_eth_private *mp, udelay(10); } - writel((phy_addr << 16) | (phy_reg << 21) | - SMI_OPCODE_WRITE | (value & 0xffff), smi_reg); + writel(SMI_OPCODE_WRITE | (reg << 21) | + (addr << 16) | (value & 0xffff), smi_reg); out: spin_unlock_irqrestore(&mp->shared_smi->phy_lock, flags); } /* mib counters *************************************************************/ -static void clear_mib_counters(struct mv643xx_eth_private *mp) +static inline u32 mib_read(struct mv643xx_eth_private *mp, int offset) { - unsigned int port_num = mp->port_num; - int i; - - /* Perform dummy reads from MIB counters */ - for (i = 0; i < 0x80; i += 4) - rdl(mp, MIB_COUNTERS(port_num) + i); + return rdl(mp, MIB_COUNTERS(mp->port_num) + offset); } -static inline u32 read_mib(struct mv643xx_eth_private *mp, int offset) +static void mib_counters_clear(struct mv643xx_eth_private *mp) { - return rdl(mp, MIB_COUNTERS(mp->port_num) + offset); + int i; + + for (i = 0; i < 0x80; i += 4) + mib_read(mp, i); } -static void update_mib_counters(struct mv643xx_eth_private *mp) +static void mib_counters_update(struct mv643xx_eth_private *mp) { struct mib_counters *p = &mp->mib_counters; - p->good_octets_received += read_mib(mp, 0x00); - p->good_octets_received += (u64)read_mib(mp, 0x04) << 32; - p->bad_octets_received += read_mib(mp, 0x08); - p->internal_mac_transmit_err += read_mib(mp, 0x0c); - p->good_frames_received += read_mib(mp, 0x10); - p->bad_frames_received += read_mib(mp, 0x14); - p->broadcast_frames_received += read_mib(mp, 0x18); - p->multicast_frames_received += read_mib(mp, 0x1c); - p->frames_64_octets += read_mib(mp, 0x20); - p->frames_65_to_127_octets += read_mib(mp, 0x24); - p->frames_128_to_255_octets += read_mib(mp, 0x28); - p->frames_256_to_511_octets += read_mib(mp, 0x2c); - p->frames_512_to_1023_octets += read_mib(mp, 0x30); - p->frames_1024_to_max_octets += read_mib(mp, 0x34); - p->good_octets_sent += read_mib(mp, 0x38); - p->good_octets_sent += (u64)read_mib(mp, 0x3c) << 32; - p->good_frames_sent += read_mib(mp, 0x40); - p->excessive_collision += read_mib(mp, 0x44); - p->multicast_frames_sent += read_mib(mp, 0x48); - p->broadcast_frames_sent += read_mib(mp, 0x4c); - p->unrec_mac_control_received += read_mib(mp, 0x50); - p->fc_sent += read_mib(mp, 0x54); - p->good_fc_received += read_mib(mp, 0x58); - p->bad_fc_received += read_mib(mp, 0x5c); - p->undersize_received += read_mib(mp, 0x60); - p->fragments_received += read_mib(mp, 0x64); - p->oversize_received += read_mib(mp, 0x68); - p->jabber_received += read_mib(mp, 0x6c); - p->mac_receive_error += read_mib(mp, 0x70); - p->bad_crc_event += read_mib(mp, 0x74); - p->collision += read_mib(mp, 0x78); - p->late_collision += read_mib(mp, 0x7c); + p->good_octets_received += mib_read(mp, 0x00); + p->good_octets_received += (u64)mib_read(mp, 0x04) << 32; + p->bad_octets_received += mib_read(mp, 0x08); + p->internal_mac_transmit_err += mib_read(mp, 0x0c); + p->good_frames_received += mib_read(mp, 0x10); + p->bad_frames_received += mib_read(mp, 0x14); + p->broadcast_frames_received += mib_read(mp, 0x18); + p->multicast_frames_received += mib_read(mp, 0x1c); + p->frames_64_octets += mib_read(mp, 0x20); + p->frames_65_to_127_octets += mib_read(mp, 0x24); + p->frames_128_to_255_octets += mib_read(mp, 0x28); + p->frames_256_to_511_octets += mib_read(mp, 0x2c); + p->frames_512_to_1023_octets += mib_read(mp, 0x30); + p->frames_1024_to_max_octets += mib_read(mp, 0x34); + p->good_octets_sent += mib_read(mp, 0x38); + p->good_octets_sent += (u64)mib_read(mp, 0x3c) << 32; + p->good_frames_sent += mib_read(mp, 0x40); + p->excessive_collision += mib_read(mp, 0x44); + p->multicast_frames_sent += mib_read(mp, 0x48); + p->broadcast_frames_sent += mib_read(mp, 0x4c); + p->unrec_mac_control_received += mib_read(mp, 0x50); + p->fc_sent += mib_read(mp, 0x54); + p->good_fc_received += mib_read(mp, 0x58); + p->bad_fc_received += mib_read(mp, 0x5c); + p->undersize_received += mib_read(mp, 0x60); + p->fragments_received += mib_read(mp, 0x64); + p->oversize_received += mib_read(mp, 0x68); + p->jabber_received += mib_read(mp, 0x6c); + p->mac_receive_error += mib_read(mp, 0x70); + p->bad_crc_event += mib_read(mp, 0x74); + p->collision += mib_read(mp, 0x78); + p->late_collision += mib_read(mp, 0x7c); } @@ -944,7 +962,9 @@ static int mv643xx_eth_get_settings(struct net_device *dev, struct ethtool_cmd * err = mii_ethtool_gset(&mp->mii, cmd); spin_unlock_irq(&mp->lock); - /* The PHY may support 1000baseT_Half, but the mv643xx does not */ + /* + * The MAC does not support 1000baseT_Half. + */ cmd->supported &= ~SUPPORTED_1000baseT_Half; cmd->advertising &= ~ADVERTISED_1000baseT_Half; @@ -956,6 +976,11 @@ static int mv643xx_eth_set_settings(struct net_device *dev, struct ethtool_cmd * struct mv643xx_eth_private *mp = netdev_priv(dev); int err; + /* + * The MAC does not support 1000baseT_Half. + */ + cmd->advertising &= ~ADVERTISED_1000baseT_Half; + spin_lock_irq(&mp->lock); err = mii_ethtool_sset(&mp->mii, cmd); spin_unlock_irq(&mp->lock); @@ -963,17 +988,17 @@ static int mv643xx_eth_set_settings(struct net_device *dev, struct ethtool_cmd * return err; } -static void mv643xx_eth_get_drvinfo(struct net_device *netdev, - struct ethtool_drvinfo *drvinfo) +static void mv643xx_eth_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *drvinfo) { strncpy(drvinfo->driver, mv643xx_eth_driver_name, 32); strncpy(drvinfo->version, mv643xx_eth_driver_version, 32); strncpy(drvinfo->fw_version, "N/A", 32); - strncpy(drvinfo->bus_info, "mv643xx", 32); + strncpy(drvinfo->bus_info, "platform", 32); drvinfo->n_stats = ARRAY_SIZE(mv643xx_eth_stats); } -static int mv643xx_eth_nway_restart(struct net_device *dev) +static int mv643xx_eth_nway_reset(struct net_device *dev) { struct mv643xx_eth_private *mp = netdev_priv(dev); @@ -987,29 +1012,28 @@ static u32 mv643xx_eth_get_link(struct net_device *dev) return mii_link_ok(&mp->mii); } -static void mv643xx_eth_get_strings(struct net_device *netdev, uint32_t stringset, - uint8_t *data) +static void mv643xx_eth_get_strings(struct net_device *dev, + uint32_t stringset, uint8_t *data) { int i; - switch(stringset) { - case ETH_SS_STATS: - for (i=0; i < ARRAY_SIZE(mv643xx_eth_stats); i++) { + if (stringset == ETH_SS_STATS) { + for (i = 0; i < ARRAY_SIZE(mv643xx_eth_stats); i++) { memcpy(data + i * ETH_GSTRING_LEN, mv643xx_eth_stats[i].stat_string, ETH_GSTRING_LEN); } - break; } } -static void mv643xx_eth_get_ethtool_stats(struct net_device *netdev, - struct ethtool_stats *stats, uint64_t *data) +static void mv643xx_eth_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *stats, + uint64_t *data) { - struct mv643xx_eth_private *mp = netdev->priv; + struct mv643xx_eth_private *mp = dev->priv; int i; - update_mib_counters(mp); + mib_counters_update(mp); for (i = 0; i < ARRAY_SIZE(mv643xx_eth_stats); i++) { const struct mv643xx_eth_stats *stat; @@ -1027,38 +1051,35 @@ static void mv643xx_eth_get_ethtool_stats(struct net_device *netdev, } } -static int mv643xx_eth_get_sset_count(struct net_device *netdev, int sset) +static int mv643xx_eth_get_sset_count(struct net_device *dev, int sset) { - switch (sset) { - case ETH_SS_STATS: + if (sset == ETH_SS_STATS) return ARRAY_SIZE(mv643xx_eth_stats); - default: - return -EOPNOTSUPP; - } + + return -EOPNOTSUPP; } static const struct ethtool_ops mv643xx_eth_ethtool_ops = { - .get_settings = mv643xx_eth_get_settings, - .set_settings = mv643xx_eth_set_settings, - .get_drvinfo = mv643xx_eth_get_drvinfo, - .get_link = mv643xx_eth_get_link, + .get_settings = mv643xx_eth_get_settings, + .set_settings = mv643xx_eth_set_settings, + .get_drvinfo = mv643xx_eth_get_drvinfo, + .nway_reset = mv643xx_eth_nway_reset, + .get_link = mv643xx_eth_get_link, .set_sg = ethtool_op_set_sg, + .get_strings = mv643xx_eth_get_strings, + .get_ethtool_stats = mv643xx_eth_get_ethtool_stats, .get_sset_count = mv643xx_eth_get_sset_count, - .get_ethtool_stats = mv643xx_eth_get_ethtool_stats, - .get_strings = mv643xx_eth_get_strings, - .nway_reset = mv643xx_eth_nway_restart, }; /* address handling *********************************************************/ static void uc_addr_get(struct mv643xx_eth_private *mp, unsigned char *addr) { - unsigned int port_num = mp->port_num; unsigned int mac_h; unsigned int mac_l; - mac_h = rdl(mp, MAC_ADDR_HIGH(port_num)); - mac_l = rdl(mp, MAC_ADDR_LOW(port_num)); + mac_h = rdl(mp, MAC_ADDR_HIGH(mp->port_num)); + mac_l = rdl(mp, MAC_ADDR_LOW(mp->port_num)); addr[0] = (mac_h >> 24) & 0xff; addr[1] = (mac_h >> 16) & 0xff; @@ -1070,72 +1091,54 @@ static void uc_addr_get(struct mv643xx_eth_private *mp, unsigned char *addr) static void init_mac_tables(struct mv643xx_eth_private *mp) { - unsigned int port_num = mp->port_num; - int table_index; - - /* Clear DA filter unicast table (Ex_dFUT) */ - for (table_index = 0; table_index <= 0xC; table_index += 4) - wrl(mp, UNICAST_TABLE(port_num) + table_index, 0); + int i; - for (table_index = 0; table_index <= 0xFC; table_index += 4) { - /* Clear DA filter special multicast table (Ex_dFSMT) */ - wrl(mp, SPECIAL_MCAST_TABLE(port_num) + table_index, 0); - /* Clear DA filter other multicast table (Ex_dFOMT) */ - wrl(mp, OTHER_MCAST_TABLE(port_num) + table_index, 0); + for (i = 0; i < 0x100; i += 4) { + wrl(mp, SPECIAL_MCAST_TABLE(mp->port_num) + i, 0); + wrl(mp, OTHER_MCAST_TABLE(mp->port_num) + i, 0); } + + for (i = 0; i < 0x10; i += 4) + wrl(mp, UNICAST_TABLE(mp->port_num) + i, 0); } static void set_filter_table_entry(struct mv643xx_eth_private *mp, - int table, unsigned char entry) + int table, unsigned char entry) { unsigned int table_reg; - unsigned int tbl_offset; - unsigned int reg_offset; - - tbl_offset = (entry / 4) * 4; /* Register offset of DA table entry */ - reg_offset = entry % 4; /* Entry offset within the register */ /* Set "accepts frame bit" at specified table entry */ - table_reg = rdl(mp, table + tbl_offset); - table_reg |= 0x01 << (8 * reg_offset); - wrl(mp, table + tbl_offset, table_reg); + table_reg = rdl(mp, table + (entry & 0xfc)); + table_reg |= 0x01 << (8 * (entry & 3)); + wrl(mp, table + (entry & 0xfc), table_reg); } static void uc_addr_set(struct mv643xx_eth_private *mp, unsigned char *addr) { - unsigned int port_num = mp->port_num; unsigned int mac_h; unsigned int mac_l; int table; - mac_l = (addr[4] << 8) | (addr[5]); - mac_h = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | - (addr[3] << 0); + mac_l = (addr[4] << 8) | addr[5]; + mac_h = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3]; - wrl(mp, MAC_ADDR_LOW(port_num), mac_l); - wrl(mp, MAC_ADDR_HIGH(port_num), mac_h); + wrl(mp, MAC_ADDR_LOW(mp->port_num), mac_l); + wrl(mp, MAC_ADDR_HIGH(mp->port_num), mac_h); - /* Accept frames with this address */ - table = UNICAST_TABLE(port_num); + table = UNICAST_TABLE(mp->port_num); set_filter_table_entry(mp, table, addr[5] & 0x0f); } -static void mv643xx_eth_update_mac_address(struct net_device *dev) +static int mv643xx_eth_set_mac_address(struct net_device *dev, void *addr) { struct mv643xx_eth_private *mp = netdev_priv(dev); + /* +2 is for the offset of the HW addr type */ + memcpy(dev->dev_addr, addr + 2, 6); + init_mac_tables(mp); uc_addr_set(mp, dev->dev_addr); -} - -static int mv643xx_eth_set_mac_address(struct net_device *dev, void *addr) -{ - int i; - for (i = 0; i < 6; i++) - /* +2 is for the offset of the HW addr type */ - dev->dev_addr[i] = ((unsigned char *)addr)[i + 2]; - mv643xx_eth_update_mac_address(dev); return 0; } @@ -1157,95 +1160,53 @@ static int addr_crc(unsigned char *addr) return crc; } -static void mc_addr(struct mv643xx_eth_private *mp, unsigned char *addr) +static void mv643xx_eth_set_rx_mode(struct net_device *dev) { - unsigned int port_num = mp->port_num; - int table; - int crc; - - if ((addr[0] == 0x01) && (addr[1] == 0x00) && - (addr[2] == 0x5E) && (addr[3] == 0x00) && (addr[4] == 0x00)) { - table = SPECIAL_MCAST_TABLE(port_num); - set_filter_table_entry(mp, table, addr[5]); - return; - } - - crc = addr_crc(addr); - - table = OTHER_MCAST_TABLE(port_num); - set_filter_table_entry(mp, table, crc); -} + struct mv643xx_eth_private *mp = netdev_priv(dev); + u32 port_config; + struct dev_addr_list *addr; + int i; -static void set_multicast_list(struct net_device *dev) -{ + port_config = rdl(mp, PORT_CONFIG(mp->port_num)); + if (dev->flags & IFF_PROMISC) + port_config |= UNICAST_PROMISCUOUS_MODE; + else + port_config &= ~UNICAST_PROMISCUOUS_MODE; + wrl(mp, PORT_CONFIG(mp->port_num), port_config); - struct dev_mc_list *mc_list; - int i; - int table_index; - struct mv643xx_eth_private *mp = netdev_priv(dev); - unsigned int port_num = mp->port_num; + if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) { + int port_num = mp->port_num; + u32 accept = 0x01010101; - /* If the device is in promiscuous mode or in all multicast mode, - * we will fully populate both multicast tables with accept. - * This is guaranteed to yield a match on all multicast addresses... - */ - if ((dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI)) { - for (table_index = 0; table_index <= 0xFC; table_index += 4) { - /* Set all entries in DA filter special multicast - * table (Ex_dFSMT) - * Set for ETH_Q0 for now - * Bits - * 0 Accept=1, Drop=0 - * 3-1 Queue ETH_Q0=0 - * 7-4 Reserved = 0; - */ - wrl(mp, SPECIAL_MCAST_TABLE(port_num) + table_index, 0x01010101); - - /* Set all entries in DA filter other multicast - * table (Ex_dFOMT) - * Set for ETH_Q0 for now - * Bits - * 0 Accept=1, Drop=0 - * 3-1 Queue ETH_Q0=0 - * 7-4 Reserved = 0; - */ - wrl(mp, OTHER_MCAST_TABLE(port_num) + table_index, 0x01010101); + for (i = 0; i < 0x100; i += 4) { + wrl(mp, SPECIAL_MCAST_TABLE(port_num) + i, accept); + wrl(mp, OTHER_MCAST_TABLE(port_num) + i, accept); } return; } - /* We will clear out multicast tables every time we get the list. - * Then add the entire new list... - */ - for (table_index = 0; table_index <= 0xFC; table_index += 4) { - /* Clear DA filter special multicast table (Ex_dFSMT) */ - wrl(mp, SPECIAL_MCAST_TABLE(port_num) + table_index, 0); - - /* Clear DA filter other multicast table (Ex_dFOMT) */ - wrl(mp, OTHER_MCAST_TABLE(port_num) + table_index, 0); + for (i = 0; i < 0x100; i += 4) { + wrl(mp, SPECIAL_MCAST_TABLE(mp->port_num) + i, 0); + wrl(mp, OTHER_MCAST_TABLE(mp->port_num) + i, 0); } - /* Get pointer to net_device multicast list and add each one... */ - for (i = 0, mc_list = dev->mc_list; - (i < 256) && (mc_list != NULL) && (i < dev->mc_count); - i++, mc_list = mc_list->next) - if (mc_list->dmi_addrlen == 6) - mc_addr(mp, mc_list->dmi_addr); -} + for (addr = dev->mc_list; addr != NULL; addr = addr->next) { + u8 *a = addr->da_addr; + int table; -static void mv643xx_eth_set_rx_mode(struct net_device *dev) -{ - struct mv643xx_eth_private *mp = netdev_priv(dev); - u32 config_reg; + if (addr->da_addrlen != 6) + continue; - config_reg = rdl(mp, PORT_CONFIG(mp->port_num)); - if (dev->flags & IFF_PROMISC) - config_reg |= UNICAST_PROMISCUOUS_MODE; - else - config_reg &= ~UNICAST_PROMISCUOUS_MODE; - wrl(mp, PORT_CONFIG(mp->port_num), config_reg); + if (memcmp(a, "\x01\x00\x5e\x00\x00", 5) == 0) { + table = SPECIAL_MCAST_TABLE(mp->port_num); + set_filter_table_entry(mp, table, a[5]); + } else { + int crc = addr_crc(a); - set_multicast_list(dev); + table = OTHER_MCAST_TABLE(mp->port_num); + set_filter_table_entry(mp, table, crc); + } + } } @@ -1483,10 +1444,7 @@ static void txq_deinit(struct tx_queue *txq) /* netdev ops and related ***************************************************/ -static void port_reset(struct mv643xx_eth_private *mp); - -static void mv643xx_eth_update_pscr(struct mv643xx_eth_private *mp, - struct ethtool_cmd *ecmd) +static void update_pscr(struct mv643xx_eth_private *mp, int speed, int duplex) { u32 pscr_o; u32 pscr_n; @@ -1499,15 +1457,15 @@ static void mv643xx_eth_update_pscr(struct mv643xx_eth_private *mp, SET_FULL_DUPLEX_MODE | MAX_RX_PACKET_MASK); - if (ecmd->speed == SPEED_1000) { + if (speed == SPEED_1000) { pscr_n |= SET_GMII_SPEED_TO_1000 | MAX_RX_PACKET_9700BYTE; } else { - if (ecmd->speed == SPEED_100) + if (speed == SPEED_100) pscr_n |= SET_MII_SPEED_TO_100; pscr_n |= MAX_RX_PACKET_1522BYTE; } - if (ecmd->duplex == DUPLEX_FULL) + if (duplex == DUPLEX_FULL) pscr_n |= SET_FULL_DUPLEX_MODE; if (pscr_n != pscr_o) { @@ -1524,27 +1482,30 @@ static void mv643xx_eth_update_pscr(struct mv643xx_eth_private *mp, } } -static irqreturn_t mv643xx_eth_int_handler(int irq, void *dev_id) +static irqreturn_t mv643xx_eth_irq(int irq, void *dev_id) { struct net_device *dev = (struct net_device *)dev_id; struct mv643xx_eth_private *mp = netdev_priv(dev); - u32 int_cause, int_cause_ext = 0; + u32 int_cause; + u32 int_cause_ext; - /* Read interrupt cause registers */ int_cause = rdl(mp, INT_CAUSE(mp->port_num)) & (INT_RX | INT_EXT); + if (int_cause == 0) + return IRQ_NONE; + + int_cause_ext = 0; if (int_cause & INT_EXT) { int_cause_ext = rdl(mp, INT_CAUSE_EXT(mp->port_num)) & (INT_EXT_LINK | INT_EXT_PHY | INT_EXT_TX); wrl(mp, INT_CAUSE_EXT(mp->port_num), ~int_cause_ext); } - /* PHY status changed */ - if (int_cause_ext & (INT_EXT_LINK | INT_EXT_PHY)) { + if (int_cause_ext & (INT_EXT_PHY | INT_EXT_LINK)) { if (mii_link_ok(&mp->mii)) { struct ethtool_cmd cmd; mii_ethtool_gset(&mp->mii, &cmd); - mv643xx_eth_update_pscr(mp, &cmd); + update_pscr(mp, cmd.speed, cmd.duplex); txq_enable(mp->txq); if (!netif_carrier_ok(dev)) { netif_carrier_on(dev); @@ -1558,10 +1519,7 @@ static irqreturn_t mv643xx_eth_int_handler(int irq, void *dev_id) #ifdef MV643XX_ETH_NAPI if (int_cause & INT_RX) { - /* schedule the NAPI poll routine to maintain port */ wrl(mp, INT_MASK(mp->port_num), 0x00000000); - - /* wait for previous write to complete */ rdl(mp, INT_MASK(mp->port_num)); netif_rx_schedule(dev, &mp->napi); @@ -1570,40 +1528,31 @@ static irqreturn_t mv643xx_eth_int_handler(int irq, void *dev_id) if (int_cause & INT_RX) rxq_process(mp->rxq, INT_MAX); #endif + if (int_cause_ext & INT_EXT_TX) { txq_reclaim(mp->txq, 0); __txq_maybe_wake(mp->txq); } - /* - * If no real interrupt occured, exit. - * This can happen when using gigE interrupt coalescing mechanism. - */ - if ((int_cause == 0x0) && (int_cause_ext == 0x0)) - return IRQ_NONE; - return IRQ_HANDLED; } static void phy_reset(struct mv643xx_eth_private *mp) { - unsigned int phy_reg_data; + unsigned int data; - /* Reset the PHY */ - read_smi_reg(mp, 0, &phy_reg_data); - phy_reg_data |= 0x8000; /* Set bit 15 to reset the PHY */ - write_smi_reg(mp, 0, phy_reg_data); + smi_reg_read(mp, mp->phy_addr, 0, &data); + data |= 0x8000; + smi_reg_write(mp, mp->phy_addr, 0, data); - /* wait for PHY to come out of reset */ do { udelay(1); - read_smi_reg(mp, 0, &phy_reg_data); - } while (phy_reg_data & 0x8000); + smi_reg_read(mp, mp->phy_addr, 0, &data); + } while (data & 0x8000); } -static void port_start(struct net_device *dev) +static void port_start(struct mv643xx_eth_private *mp) { - struct mv643xx_eth_private *mp = netdev_priv(dev); u32 pscr; struct ethtool_cmd ethtool_cmd; int i; @@ -1625,9 +1574,9 @@ static void port_start(struct net_device *dev) wrl(mp, SDMA_CONFIG(mp->port_num), PORT_SDMA_CONFIG_DEFAULT_VALUE); - mv643xx_eth_get_settings(dev, ðtool_cmd); + mv643xx_eth_get_settings(mp->dev, ðtool_cmd); phy_reset(mp); - mv643xx_eth_set_settings(dev, ðtool_cmd); + mv643xx_eth_set_settings(mp->dev, ðtool_cmd); /* * Configure TX path and queues. @@ -1643,8 +1592,10 @@ static void port_start(struct net_device *dev) wrl(mp, off, addr); } - /* Add the assigned Ethernet address to the port's address table */ - uc_addr_set(mp, dev->dev_addr); + /* + * Add configured unicast address to address filter table. + */ + uc_addr_set(mp, mp->dev->dev_addr); /* * Receive all unmatched unicast, TCP, UDP, BPDU and broadcast @@ -1675,13 +1626,14 @@ static void port_start(struct net_device *dev) static void set_rx_coal(struct mv643xx_eth_private *mp, unsigned int delay) { - unsigned int port_num = mp->port_num; unsigned int coal = ((mp->shared->t_clk / 1000000) * delay) / 64; - /* Set RX Coalescing mechanism */ - wrl(mp, SDMA_CONFIG(port_num), + if (coal > 0x3fff) + coal = 0x3fff; + + wrl(mp, SDMA_CONFIG(mp->port_num), ((coal & 0x3fff) << 8) | - (rdl(mp, SDMA_CONFIG(port_num)) + (rdl(mp, SDMA_CONFIG(mp->port_num)) & 0xffc000ff)); } @@ -1689,68 +1641,59 @@ static void set_tx_coal(struct mv643xx_eth_private *mp, unsigned int delay) { unsigned int coal = ((mp->shared->t_clk / 1000000) * delay) / 64; - /* Set TX Coalescing mechanism */ - wrl(mp, TX_FIFO_URGENT_THRESHOLD(mp->port_num), coal << 4); -} - -static void port_init(struct mv643xx_eth_private *mp) -{ - port_reset(mp); - - init_mac_tables(mp); + if (coal > 0x3fff) + coal = 0x3fff; + wrl(mp, TX_FIFO_URGENT_THRESHOLD(mp->port_num), (coal & 0x3fff) << 4); } static int mv643xx_eth_open(struct net_device *dev) { struct mv643xx_eth_private *mp = netdev_priv(dev); - unsigned int port_num = mp->port_num; int err; - /* Clear any pending ethernet port interrupts */ - wrl(mp, INT_CAUSE(port_num), 0); - wrl(mp, INT_CAUSE_EXT(port_num), 0); - /* wait for previous write to complete */ - rdl(mp, INT_CAUSE_EXT(port_num)); + wrl(mp, INT_CAUSE(mp->port_num), 0); + wrl(mp, INT_CAUSE_EXT(mp->port_num), 0); + rdl(mp, INT_CAUSE_EXT(mp->port_num)); - err = request_irq(dev->irq, mv643xx_eth_int_handler, - IRQF_SHARED | IRQF_SAMPLE_RANDOM, dev->name, dev); + err = request_irq(dev->irq, mv643xx_eth_irq, + IRQF_SHARED | IRQF_SAMPLE_RANDOM, + dev->name, dev); if (err) { - printk(KERN_ERR "%s: Can not assign IRQ\n", dev->name); + dev_printk(KERN_ERR, &dev->dev, "can't assign irq\n"); return -EAGAIN; } - port_init(mp); + init_mac_tables(mp); err = rxq_init(mp); if (err) - goto out_free_irq; + goto out; rxq_refill(mp->rxq); err = txq_init(mp); if (err) - goto out_free_rx_skb; + goto out_free; #ifdef MV643XX_ETH_NAPI napi_enable(&mp->napi); #endif - port_start(dev); + port_start(mp); set_rx_coal(mp, 0); set_tx_coal(mp, 0); - /* Unmask phy and link status changes interrupts */ - wrl(mp, INT_MASK_EXT(port_num), INT_EXT_LINK | INT_EXT_PHY | INT_EXT_TX); + wrl(mp, INT_MASK_EXT(mp->port_num), + INT_EXT_LINK | INT_EXT_PHY | INT_EXT_TX); - /* Unmask RX buffer and TX end interrupt */ - wrl(mp, INT_MASK(port_num), INT_RX | INT_EXT); + wrl(mp, INT_MASK(mp->port_num), INT_RX | INT_EXT); return 0; -out_free_rx_skb: +out_free: rxq_deinit(mp->rxq); -out_free_irq: +out: free_irq(dev->irq, dev); return err; @@ -1758,34 +1701,27 @@ out_free_irq: static void port_reset(struct mv643xx_eth_private *mp) { - unsigned int port_num = mp->port_num; - unsigned int reg_data; + unsigned int data; txq_disable(mp->txq); rxq_disable(mp->rxq); while (!(rdl(mp, PORT_STATUS(mp->port_num)) & TX_FIFO_EMPTY)) udelay(10); - /* Clear all MIB counters */ - clear_mib_counters(mp); - /* Reset the Enable bit in the Configuration Register */ - reg_data = rdl(mp, PORT_SERIAL_CONTROL(port_num)); - reg_data &= ~(SERIAL_PORT_ENABLE | - DO_NOT_FORCE_LINK_FAIL | - FORCE_LINK_PASS); - wrl(mp, PORT_SERIAL_CONTROL(port_num), reg_data); + data = rdl(mp, PORT_SERIAL_CONTROL(mp->port_num)); + data &= ~(SERIAL_PORT_ENABLE | + DO_NOT_FORCE_LINK_FAIL | + FORCE_LINK_PASS); + wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), data); } static int mv643xx_eth_stop(struct net_device *dev) { struct mv643xx_eth_private *mp = netdev_priv(dev); - unsigned int port_num = mp->port_num; - /* Mask all interrupts on ethernet port */ - wrl(mp, INT_MASK(port_num), 0x00000000); - /* wait for previous write to complete */ - rdl(mp, INT_MASK(port_num)); + wrl(mp, INT_MASK(mp->port_num), 0x00000000); + rdl(mp, INT_MASK(mp->port_num)); #ifdef MV643XX_ETH_NAPI napi_disable(&mp->napi); @@ -1793,17 +1729,18 @@ static int mv643xx_eth_stop(struct net_device *dev) netif_carrier_off(dev); netif_stop_queue(dev); + free_irq(dev->irq, dev); + port_reset(mp); + mib_counters_update(mp); txq_deinit(mp->txq); rxq_deinit(mp->rxq); - free_irq(dev->irq, dev); - return 0; } -static int mv643xx_eth_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +static int mv643xx_eth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { struct mv643xx_eth_private *mp = netdev_priv(dev); @@ -1812,7 +1749,7 @@ static int mv643xx_eth_do_ioctl(struct net_device *dev, struct ifreq *ifr, int c static int mv643xx_eth_change_mtu(struct net_device *dev, int new_mtu) { - if ((new_mtu > 9500) || (new_mtu < 64)) + if (new_mtu < 64 || new_mtu > 9500) return -EINVAL; dev->mtu = new_mtu; @@ -1823,73 +1760,70 @@ static int mv643xx_eth_change_mtu(struct net_device *dev, int new_mtu) * Stop and then re-open the interface. This will allocate RX * skbs of the new MTU. * There is a possible danger that the open will not succeed, - * due to memory being full, which might fail the open function. + * due to memory being full. */ mv643xx_eth_stop(dev); if (mv643xx_eth_open(dev)) { - printk(KERN_ERR "%s: Fatal error on opening device\n", - dev->name); + dev_printk(KERN_ERR, &dev->dev, + "fatal error on re-opening device after " + "MTU change\n"); } return 0; } -static void mv643xx_eth_tx_timeout_task(struct work_struct *ugly) +static void tx_timeout_task(struct work_struct *ugly) { - struct mv643xx_eth_private *mp = container_of(ugly, struct mv643xx_eth_private, - tx_timeout_task); - struct net_device *dev = mp->dev; - - if (!netif_running(dev)) - return; + struct mv643xx_eth_private *mp; - netif_stop_queue(dev); + mp = container_of(ugly, struct mv643xx_eth_private, tx_timeout_task); + if (netif_running(mp->dev)) { + netif_stop_queue(mp->dev); - port_reset(mp); - port_start(dev); + port_reset(mp); + port_start(mp); - __txq_maybe_wake(mp->txq); + __txq_maybe_wake(mp->txq); + } } static void mv643xx_eth_tx_timeout(struct net_device *dev) { struct mv643xx_eth_private *mp = netdev_priv(dev); - printk(KERN_INFO "%s: TX timeout ", dev->name); + dev_printk(KERN_INFO, &dev->dev, "tx timeout\n"); - /* Do the reset outside of interrupt context */ schedule_work(&mp->tx_timeout_task); } #ifdef CONFIG_NET_POLL_CONTROLLER -static void mv643xx_eth_netpoll(struct net_device *netdev) +static void mv643xx_eth_netpoll(struct net_device *dev) { - struct mv643xx_eth_private *mp = netdev_priv(netdev); - int port_num = mp->port_num; + struct mv643xx_eth_private *mp = netdev_priv(dev); - wrl(mp, INT_MASK(port_num), 0x00000000); - /* wait for previous write to complete */ - rdl(mp, INT_MASK(port_num)); + wrl(mp, INT_MASK(mp->port_num), 0x00000000); + rdl(mp, INT_MASK(mp->port_num)); - mv643xx_eth_int_handler(netdev->irq, netdev); + mv643xx_eth_irq(dev->irq, dev); - wrl(mp, INT_MASK(port_num), INT_RX | INT_CAUSE_EXT); + wrl(mp, INT_MASK(mp->port_num), INT_RX | INT_CAUSE_EXT); } #endif -static int mv643xx_eth_mdio_read(struct net_device *dev, int phy_id, int location) +static int mv643xx_eth_mdio_read(struct net_device *dev, int addr, int reg) { struct mv643xx_eth_private *mp = netdev_priv(dev); int val; - read_smi_reg(mp, location, &val); + smi_reg_read(mp, addr, reg, &val); + return val; } -static void mv643xx_eth_mdio_write(struct net_device *dev, int phy_id, int location, int val) +static void mv643xx_eth_mdio_write(struct net_device *dev, int addr, int reg, int val) { struct mv643xx_eth_private *mp = netdev_priv(dev); - write_smi_reg(mp, location, val); + smi_reg_write(mp, addr, reg, val); } @@ -1956,9 +1890,6 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev) goto out_free; spin_lock_init(&msp->phy_lock); - msp->t_clk = (pd != NULL && pd->t_clk != 0) ? pd->t_clk : 133000000; - - platform_set_drvdata(pdev, msp); /* * (Re-)program MBUS remapping windows if we are asked to. @@ -1966,6 +1897,13 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev) if (pd != NULL && pd->dram != NULL) mv643xx_eth_conf_mbus_windows(msp, pd->dram); + /* + * Detect hardware parameters. + */ + msp->t_clk = (pd != NULL && pd->t_clk != 0) ? pd->t_clk : 133000000; + + platform_set_drvdata(pdev, msp); + return 0; out_free: @@ -1985,104 +1923,158 @@ static int mv643xx_eth_shared_remove(struct platform_device *pdev) } static struct platform_driver mv643xx_eth_shared_driver = { - .probe = mv643xx_eth_shared_probe, - .remove = mv643xx_eth_shared_remove, + .probe = mv643xx_eth_shared_probe, + .remove = mv643xx_eth_shared_remove, .driver = { - .name = MV643XX_ETH_SHARED_NAME, + .name = MV643XX_ETH_SHARED_NAME, .owner = THIS_MODULE, }, }; static void phy_addr_set(struct mv643xx_eth_private *mp, int phy_addr) { - u32 reg_data; int addr_shift = 5 * mp->port_num; + u32 data; - reg_data = rdl(mp, PHY_ADDR); - reg_data &= ~(0x1f << addr_shift); - reg_data |= (phy_addr & 0x1f) << addr_shift; - wrl(mp, PHY_ADDR, reg_data); + data = rdl(mp, PHY_ADDR); + data &= ~(0x1f << addr_shift); + data |= (phy_addr & 0x1f) << addr_shift; + wrl(mp, PHY_ADDR, data); } static int phy_addr_get(struct mv643xx_eth_private *mp) { - unsigned int reg_data; + unsigned int data; + + data = rdl(mp, PHY_ADDR); + + return (data >> (5 * mp->port_num)) & 0x1f; +} + +static void set_params(struct mv643xx_eth_private *mp, + struct mv643xx_eth_platform_data *pd) +{ + struct net_device *dev = mp->dev; + + if (is_valid_ether_addr(pd->mac_addr)) + memcpy(dev->dev_addr, pd->mac_addr, 6); + else + uc_addr_get(mp, dev->dev_addr); + + if (pd->phy_addr == -1) { + mp->shared_smi = NULL; + mp->phy_addr = -1; + } else { + mp->shared_smi = mp->shared; + if (pd->shared_smi != NULL) + mp->shared_smi = platform_get_drvdata(pd->shared_smi); + + if (pd->force_phy_addr || pd->phy_addr) { + mp->phy_addr = pd->phy_addr & 0x3f; + phy_addr_set(mp, mp->phy_addr); + } else { + mp->phy_addr = phy_addr_get(mp); + } + } - reg_data = rdl(mp, PHY_ADDR); + mp->default_rx_ring_size = DEFAULT_RX_QUEUE_SIZE; + if (pd->rx_queue_size) + mp->default_rx_ring_size = pd->rx_queue_size; + mp->rx_desc_sram_addr = pd->rx_sram_addr; + mp->rx_desc_sram_size = pd->rx_sram_size; - return ((reg_data >> (5 * mp->port_num)) & 0x1f); + mp->default_tx_ring_size = DEFAULT_TX_QUEUE_SIZE; + if (pd->tx_queue_size) + mp->default_tx_ring_size = pd->tx_queue_size; + mp->tx_desc_sram_addr = pd->tx_sram_addr; + mp->tx_desc_sram_size = pd->tx_sram_size; } static int phy_detect(struct mv643xx_eth_private *mp) { - unsigned int phy_reg_data0; - int auto_neg; + unsigned int data; + unsigned int data2; + + smi_reg_read(mp, mp->phy_addr, 0, &data); + smi_reg_write(mp, mp->phy_addr, 0, data ^ 0x1000); - read_smi_reg(mp, 0, &phy_reg_data0); - auto_neg = phy_reg_data0 & 0x1000; - phy_reg_data0 ^= 0x1000; /* invert auto_neg */ - write_smi_reg(mp, 0, phy_reg_data0); + smi_reg_read(mp, mp->phy_addr, 0, &data2); + if (((data ^ data2) & 0x1000) == 0) + return -ENODEV; - read_smi_reg(mp, 0, &phy_reg_data0); - if ((phy_reg_data0 & 0x1000) == auto_neg) - return -ENODEV; /* change didn't take */ + smi_reg_write(mp, mp->phy_addr, 0, data); - phy_reg_data0 ^= 0x1000; - write_smi_reg(mp, 0, phy_reg_data0); return 0; } -static void mv643xx_init_ethtool_cmd(struct net_device *dev, int phy_address, - int speed, int duplex, - struct ethtool_cmd *cmd) +static int phy_init(struct mv643xx_eth_private *mp, + struct mv643xx_eth_platform_data *pd) { - struct mv643xx_eth_private *mp = netdev_priv(dev); + struct ethtool_cmd cmd; + int err; - memset(cmd, 0, sizeof(*cmd)); + err = phy_detect(mp); + if (err) { + dev_printk(KERN_INFO, &mp->dev->dev, + "no PHY detected at addr %d\n", mp->phy_addr); + return err; + } + phy_reset(mp); + + mp->mii.phy_id = mp->phy_addr; + mp->mii.phy_id_mask = 0x3f; + mp->mii.reg_num_mask = 0x1f; + mp->mii.dev = mp->dev; + mp->mii.mdio_read = mv643xx_eth_mdio_read; + mp->mii.mdio_write = mv643xx_eth_mdio_write; - cmd->port = PORT_MII; - cmd->transceiver = XCVR_INTERNAL; - cmd->phy_address = phy_address; + mp->mii.supports_gmii = mii_check_gmii_support(&mp->mii); - if (speed == 0) { - cmd->autoneg = AUTONEG_ENABLE; - /* mii lib checks, but doesn't use speed on AUTONEG_ENABLE */ - cmd->speed = SPEED_100; - cmd->advertising = ADVERTISED_10baseT_Half | - ADVERTISED_10baseT_Full | - ADVERTISED_100baseT_Half | - ADVERTISED_100baseT_Full; + memset(&cmd, 0, sizeof(cmd)); + + cmd.port = PORT_MII; + cmd.transceiver = XCVR_INTERNAL; + cmd.phy_address = mp->phy_addr; + if (pd->speed == 0) { + cmd.autoneg = AUTONEG_ENABLE; + cmd.speed = SPEED_100; + cmd.advertising = ADVERTISED_10baseT_Half | + ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | + ADVERTISED_100baseT_Full; if (mp->mii.supports_gmii) - cmd->advertising |= ADVERTISED_1000baseT_Full; + cmd.advertising |= ADVERTISED_1000baseT_Full; } else { - cmd->autoneg = AUTONEG_DISABLE; - cmd->speed = speed; - cmd->duplex = duplex; + cmd.autoneg = AUTONEG_DISABLE; + cmd.speed = pd->speed; + cmd.duplex = pd->duplex; } + + update_pscr(mp, cmd.speed, cmd.duplex); + mv643xx_eth_set_settings(mp->dev, &cmd); + + return 0; } static int mv643xx_eth_probe(struct platform_device *pdev) { struct mv643xx_eth_platform_data *pd; - int port_num; struct mv643xx_eth_private *mp; struct net_device *dev; - u8 *p; struct resource *res; - int err; - struct ethtool_cmd cmd; - int duplex = DUPLEX_HALF; - int speed = 0; /* default to auto-negotiation */ DECLARE_MAC_BUF(mac); + int err; pd = pdev->dev.platform_data; if (pd == NULL) { - printk(KERN_ERR "No mv643xx_eth_platform_data\n"); + dev_printk(KERN_ERR, &pdev->dev, + "no mv643xx_eth_platform_data\n"); return -ENODEV; } if (pd->shared == NULL) { - printk(KERN_ERR "No mv643xx_eth_platform_data->shared\n"); + dev_printk(KERN_ERR, &pdev->dev, + "no mv643xx_eth_platform_data->shared\n"); return -ENODEV; } @@ -2090,145 +2082,80 @@ static int mv643xx_eth_probe(struct platform_device *pdev) if (!dev) return -ENOMEM; - platform_set_drvdata(pdev, dev); - mp = netdev_priv(dev); + platform_set_drvdata(pdev, mp); + + mp->shared = platform_get_drvdata(pd->shared); + mp->port_num = pd->port_number; + mp->dev = dev; #ifdef MV643XX_ETH_NAPI netif_napi_add(dev, &mp->napi, mv643xx_eth_poll, 64); #endif + set_params(mp, pd); + + spin_lock_init(&mp->lock); + + mib_counters_clear(mp); + INIT_WORK(&mp->tx_timeout_task, tx_timeout_task); + + err = phy_init(mp, pd); + if (err) + goto out; + SET_ETHTOOL_OPS(dev, &mv643xx_eth_ethtool_ops); + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); BUG_ON(!res); dev->irq = res->start; + dev->hard_start_xmit = mv643xx_eth_xmit; dev->open = mv643xx_eth_open; dev->stop = mv643xx_eth_stop; - dev->hard_start_xmit = mv643xx_eth_start_xmit; - dev->set_mac_address = mv643xx_eth_set_mac_address; dev->set_multicast_list = mv643xx_eth_set_rx_mode; - - /* No need to Tx Timeout */ + dev->set_mac_address = mv643xx_eth_set_mac_address; + dev->do_ioctl = mv643xx_eth_ioctl; + dev->change_mtu = mv643xx_eth_change_mtu; dev->tx_timeout = mv643xx_eth_tx_timeout; - #ifdef CONFIG_NET_POLL_CONTROLLER dev->poll_controller = mv643xx_eth_netpoll; #endif - dev->watchdog_timeo = 2 * HZ; dev->base_addr = 0; - dev->change_mtu = mv643xx_eth_change_mtu; - dev->do_ioctl = mv643xx_eth_do_ioctl; - SET_ETHTOOL_OPS(dev, &mv643xx_eth_ethtool_ops); #ifdef MV643XX_ETH_CHECKSUM_OFFLOAD_TX -#ifdef MAX_SKB_FRAGS /* * Zero copy can only work if we use Discovery II memory. Else, we will * have to map the buffers to ISA memory which is only 16 MB */ dev->features = NETIF_F_SG | NETIF_F_IP_CSUM; -#endif #endif - /* Configure the timeout task */ - INIT_WORK(&mp->tx_timeout_task, mv643xx_eth_tx_timeout_task); - - spin_lock_init(&mp->lock); - - mp->shared = platform_get_drvdata(pd->shared); - port_num = mp->port_num = pd->port_number; + SET_NETDEV_DEV(dev, &pdev->dev); if (mp->shared->win_protect) - wrl(mp, WINDOW_PROTECT(port_num), mp->shared->win_protect); - - mp->shared_smi = mp->shared; - if (pd->shared_smi != NULL) - mp->shared_smi = platform_get_drvdata(pd->shared_smi); - - /* set default config values */ - uc_addr_get(mp, dev->dev_addr); - - if (is_valid_ether_addr(pd->mac_addr)) - memcpy(dev->dev_addr, pd->mac_addr, 6); - - if (pd->phy_addr || pd->force_phy_addr) - phy_addr_set(mp, pd->phy_addr); - - mp->default_rx_ring_size = DEFAULT_RX_QUEUE_SIZE; - if (pd->rx_queue_size) - mp->default_rx_ring_size = pd->rx_queue_size; - - mp->default_tx_ring_size = DEFAULT_TX_QUEUE_SIZE; - if (pd->tx_queue_size) - mp->default_tx_ring_size = pd->tx_queue_size; - - if (pd->tx_sram_size) { - mp->tx_desc_sram_size = pd->tx_sram_size; - mp->tx_desc_sram_addr = pd->tx_sram_addr; - } - - if (pd->rx_sram_size) { - mp->rx_desc_sram_addr = pd->rx_sram_addr; - mp->rx_desc_sram_size = pd->rx_sram_size; - } - - duplex = pd->duplex; - speed = pd->speed; + wrl(mp, WINDOW_PROTECT(mp->port_num), mp->shared->win_protect); - /* Hook up MII support for ethtool */ - mp->mii.dev = dev; - mp->mii.mdio_read = mv643xx_eth_mdio_read; - mp->mii.mdio_write = mv643xx_eth_mdio_write; - mp->mii.phy_id = phy_addr_get(mp); - mp->mii.phy_id_mask = 0x3f; - mp->mii.reg_num_mask = 0x1f; - - err = phy_detect(mp); - if (err) { - pr_debug("%s: No PHY detected at addr %d\n", - dev->name, phy_addr_get(mp)); - goto out; - } - - phy_reset(mp); - mp->mii.supports_gmii = mii_check_gmii_support(&mp->mii); - mv643xx_init_ethtool_cmd(dev, mp->mii.phy_id, speed, duplex, &cmd); - mv643xx_eth_update_pscr(mp, &cmd); - mv643xx_eth_set_settings(dev, &cmd); - - SET_NETDEV_DEV(dev, &pdev->dev); err = register_netdev(dev); if (err) goto out; - p = dev->dev_addr; - printk(KERN_NOTICE - "%s: port %d with MAC address %s\n", - dev->name, port_num, print_mac(mac, p)); + dev_printk(KERN_NOTICE, &dev->dev, "port %d with MAC address %s\n", + mp->port_num, print_mac(mac, dev->dev_addr)); if (dev->features & NETIF_F_SG) - printk(KERN_NOTICE "%s: Scatter Gather Enabled\n", dev->name); + dev_printk(KERN_NOTICE, &dev->dev, "scatter/gather enabled\n"); if (dev->features & NETIF_F_IP_CSUM) - printk(KERN_NOTICE "%s: TX TCP/IP Checksumming Supported\n", - dev->name); - -#ifdef MV643XX_ETH_CHECKSUM_OFFLOAD_TX - printk(KERN_NOTICE "%s: RX TCP/UDP Checksum Offload ON \n", dev->name); -#endif - -#ifdef MV643XX_ETH_COAL - printk(KERN_NOTICE "%s: TX and RX Interrupt Coalescing ON \n", - dev->name); -#endif + dev_printk(KERN_NOTICE, &dev->dev, "tx checksum offload\n"); #ifdef MV643XX_ETH_NAPI - printk(KERN_NOTICE "%s: RX NAPI Enabled \n", dev->name); + dev_printk(KERN_NOTICE, &dev->dev, "napi enabled\n"); #endif if (mp->tx_desc_sram_size > 0) - printk(KERN_NOTICE "%s: Using SRAM\n", dev->name); + dev_printk(KERN_NOTICE, &dev->dev, "configured with sram\n"); return 0; @@ -2240,35 +2167,35 @@ out: static int mv643xx_eth_remove(struct platform_device *pdev) { - struct net_device *dev = platform_get_drvdata(pdev); + struct mv643xx_eth_private *mp = platform_get_drvdata(pdev); - unregister_netdev(dev); + unregister_netdev(mp->dev); flush_scheduled_work(); + free_netdev(mp->dev); - free_netdev(dev); platform_set_drvdata(pdev, NULL); + return 0; } static void mv643xx_eth_shutdown(struct platform_device *pdev) { - struct net_device *dev = platform_get_drvdata(pdev); - struct mv643xx_eth_private *mp = netdev_priv(dev); - unsigned int port_num = mp->port_num; + struct mv643xx_eth_private *mp = platform_get_drvdata(pdev); /* Mask all interrupts on ethernet port */ - wrl(mp, INT_MASK(port_num), 0); - rdl(mp, INT_MASK(port_num)); + wrl(mp, INT_MASK(mp->port_num), 0); + rdl(mp, INT_MASK(mp->port_num)); - port_reset(mp); + if (netif_running(mp->dev)) + port_reset(mp); } static struct platform_driver mv643xx_eth_driver = { - .probe = mv643xx_eth_probe, - .remove = mv643xx_eth_remove, - .shutdown = mv643xx_eth_shutdown, + .probe = mv643xx_eth_probe, + .remove = mv643xx_eth_remove, + .shutdown = mv643xx_eth_shutdown, .driver = { - .name = MV643XX_ETH_NAME, + .name = MV643XX_ETH_NAME, .owner = THIS_MODULE, }, }; @@ -2283,21 +2210,21 @@ static int __init mv643xx_eth_init_module(void) if (rc) platform_driver_unregister(&mv643xx_eth_shared_driver); } + return rc; } +module_init(mv643xx_eth_init_module); static void __exit mv643xx_eth_cleanup_module(void) { platform_driver_unregister(&mv643xx_eth_driver); platform_driver_unregister(&mv643xx_eth_shared_driver); } - -module_init(mv643xx_eth_init_module); module_exit(mv643xx_eth_cleanup_module); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR( "Rabeeh Khoury, Assaf Hoffman, Matthew Dharm, Manish Lachwani" - " and Dale Farnsworth"); +MODULE_AUTHOR("Rabeeh Khoury, Assaf Hoffman, Matthew Dharm, Manish Lachwani " + "and Dale Farnsworth"); MODULE_DESCRIPTION("Ethernet driver for Marvell MV643XX"); -MODULE_ALIAS("platform:" MV643XX_ETH_NAME); +MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" MV643XX_ETH_SHARED_NAME); +MODULE_ALIAS("platform:" MV643XX_ETH_NAME); diff --git a/include/linux/mv643xx_eth.h b/include/linux/mv643xx_eth.h index a15cdd4a8e58..646177660495 100644 --- a/include/linux/mv643xx_eth.h +++ b/include/linux/mv643xx_eth.h @@ -17,30 +17,53 @@ struct mv643xx_eth_shared_platform_data { struct mbus_dram_target_info *dram; - unsigned int t_clk; + unsigned int t_clk; }; struct mv643xx_eth_platform_data { + /* + * Pointer back to our parent instance, and our port number. + */ struct platform_device *shared; - int port_number; + int port_number; + /* + * Whether a PHY is present, and if yes, at which address. + */ struct platform_device *shared_smi; + int force_phy_addr; + int phy_addr; - u16 force_phy_addr; /* force override if phy_addr == 0 */ - u16 phy_addr; - - /* If speed is 0, then speed and duplex are autonegotiated. */ - int speed; /* 0, SPEED_10, SPEED_100, SPEED_1000 */ - int duplex; /* DUPLEX_HALF or DUPLEX_FULL */ - - /* non-zero values of the following fields override defaults */ - u32 tx_queue_size; - u32 rx_queue_size; - u32 tx_sram_addr; - u32 tx_sram_size; - u32 rx_sram_addr; - u32 rx_sram_size; - u8 mac_addr[6]; /* mac address if non-zero*/ + /* + * Use this MAC address if it is valid, overriding the + * address that is already in the hardware. + */ + u8 mac_addr[6]; + + /* + * If speed is 0, autonegotiation is enabled. + * Valid values for speed: 0, SPEED_10, SPEED_100, SPEED_1000. + * Valid values for duplex: DUPLEX_HALF, DUPLEX_FULL. + */ + int speed; + int duplex; + + /* + * Override default RX/TX queue sizes if nonzero. + */ + int rx_queue_size; + int tx_queue_size; + + /* + * Use on-chip SRAM for RX/TX descriptors if size is nonzero + * and sufficient to contain all descriptors for the requested + * ring sizes. + */ + unsigned long rx_sram_addr; + int rx_sram_size; + unsigned long tx_sram_addr; + int tx_sram_size; }; -#endif /* __LINUX_MV643XX_ETH_H */ + +#endif -- cgit v1.2.3 From 64da80a29c7455321a7df7b47e27d639e3944c1a Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 2 Jun 2008 01:01:26 +0200 Subject: mv643xx_eth: allow multiple RX queues Allow the platform code to specify that we are running on hardware that is capable of supporting multiple RX queues. If this option is used, initialise all of the given RX queues instead of just RX queue zero. Signed-off-by: Lennert Buytenhek Acked-by: Dale Farnsworth --- drivers/net/mv643xx_eth.c | 99 +++++++++++++++++++++++++++++++++------------ include/linux/mv643xx_eth.h | 5 +++ 2 files changed, 79 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index 9ce7be09e295..3c8591853999 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -96,7 +96,7 @@ static char mv643xx_eth_driver_version[] = "1.0"; #define TX_BW_MTU(p) (0x0458 + ((p) << 10)) #define TX_BW_BURST(p) (0x045c + ((p) << 10)) #define INT_CAUSE(p) (0x0460 + ((p) << 10)) -#define INT_RX 0x00000804 +#define INT_RX 0x0007fbfc #define INT_EXT 0x00000002 #define INT_CAUSE_EXT(p) (0x0464 + ((p) << 10)) #define INT_EXT_LINK 0x00100000 @@ -107,7 +107,7 @@ static char mv643xx_eth_driver_version[] = "1.0"; #define INT_MASK(p) (0x0468 + ((p) << 10)) #define INT_MASK_EXT(p) (0x046c + ((p) << 10)) #define TX_FIFO_URGENT_THRESHOLD(p) (0x0474 + ((p) << 10)) -#define RXQ_CURRENT_DESC_PTR(p) (0x060c + ((p) << 10)) +#define RXQ_CURRENT_DESC_PTR(p, q) (0x060c + ((p) << 10) + ((q) << 4)) #define RXQ_COMMAND(p) (0x0680 + ((p) << 10)) #define TXQ_CURRENT_DESC_PTR(p) (0x06c0 + ((p) << 10)) #define TXQ_BW_TOKENS(p) (0x0700 + ((p) << 10)) @@ -286,6 +286,8 @@ struct mib_counters { }; struct rx_queue { + int index; + int rx_ring_size; int rx_desc_count; @@ -334,8 +336,10 @@ struct mv643xx_eth_private { int default_rx_ring_size; unsigned long rx_desc_sram_addr; int rx_desc_sram_size; + u8 rxq_mask; + int rxq_primary; struct napi_struct napi; - struct rx_queue rxq[1]; + struct rx_queue rxq[8]; /* * TX state. @@ -365,7 +369,7 @@ static inline void wrl(struct mv643xx_eth_private *mp, int offset, u32 data) /* rxq/txq helper functions *************************************************/ static struct mv643xx_eth_private *rxq_to_mp(struct rx_queue *rxq) { - return container_of(rxq, struct mv643xx_eth_private, rxq[0]); + return container_of(rxq, struct mv643xx_eth_private, rxq[rxq->index]); } static struct mv643xx_eth_private *txq_to_mp(struct tx_queue *txq) @@ -376,13 +380,13 @@ static struct mv643xx_eth_private *txq_to_mp(struct tx_queue *txq) static void rxq_enable(struct rx_queue *rxq) { struct mv643xx_eth_private *mp = rxq_to_mp(rxq); - wrl(mp, RXQ_COMMAND(mp->port_num), 1); + wrl(mp, RXQ_COMMAND(mp->port_num), 1 << rxq->index); } static void rxq_disable(struct rx_queue *rxq) { struct mv643xx_eth_private *mp = rxq_to_mp(rxq); - u8 mask = 1; + u8 mask = 1 << rxq->index; wrl(mp, RXQ_COMMAND(mp->port_num), mask << 8); while (rdl(mp, RXQ_COMMAND(mp->port_num)) & mask) @@ -583,6 +587,7 @@ static int mv643xx_eth_poll(struct napi_struct *napi, int budget) { struct mv643xx_eth_private *mp; int rx; + int i; mp = container_of(napi, struct mv643xx_eth_private, napi); @@ -593,7 +598,10 @@ static int mv643xx_eth_poll(struct napi_struct *napi, int budget) } #endif - rx = rxq_process(mp->rxq, budget); + rx = 0; + for (i = 7; rx < budget && i >= 0; i--) + if (mp->rxq_mask & (1 << i)) + rx += rxq_process(mp->rxq + i, budget - rx); if (rx < budget) { netif_rx_complete(mp->dev, napi); @@ -1306,13 +1314,15 @@ static void mv643xx_eth_set_rx_mode(struct net_device *dev) /* rx/tx queue initialisation ***********************************************/ -static int rxq_init(struct mv643xx_eth_private *mp) +static int rxq_init(struct mv643xx_eth_private *mp, int index) { - struct rx_queue *rxq = mp->rxq; + struct rx_queue *rxq = mp->rxq + index; struct rx_desc *rx_desc; int size; int i; + rxq->index = index; + rxq->rx_ring_size = mp->default_rx_ring_size; rxq->rx_desc_count = 0; @@ -1321,7 +1331,7 @@ static int rxq_init(struct mv643xx_eth_private *mp) size = rxq->rx_ring_size * sizeof(struct rx_desc); - if (size <= mp->rx_desc_sram_size) { + if (index == mp->rxq_primary && size <= mp->rx_desc_sram_size) { rxq->rx_desc_area = ioremap(mp->rx_desc_sram_addr, mp->rx_desc_sram_size); rxq->rx_desc_dma = mp->rx_desc_sram_addr; @@ -1362,7 +1372,7 @@ static int rxq_init(struct mv643xx_eth_private *mp) out_free: - if (size <= mp->rx_desc_sram_size) + if (index == mp->rxq_primary && size <= mp->rx_desc_sram_size) iounmap(rxq->rx_desc_area); else dma_free_coherent(NULL, size, @@ -1395,7 +1405,8 @@ static void rxq_deinit(struct rx_queue *rxq) rxq->rx_desc_count); } - if (rxq->rx_desc_area_size <= mp->rx_desc_sram_size) + if (rxq->index == mp->rxq_primary && + rxq->rx_desc_area_size <= mp->rx_desc_sram_size) iounmap(rxq->rx_desc_area); else dma_free_coherent(NULL, rxq->rx_desc_area_size, @@ -1612,6 +1623,9 @@ static irqreturn_t mv643xx_eth_irq(int irq, void *dev_id) } } + /* + * RxBuffer or RxError set for any of the 8 queues? + */ #ifdef MV643XX_ETH_NAPI if (int_cause & INT_RX) { wrl(mp, INT_MASK(mp->port_num), 0x00000000); @@ -1620,8 +1634,13 @@ static irqreturn_t mv643xx_eth_irq(int irq, void *dev_id) netif_rx_schedule(dev, &mp->napi); } #else - if (int_cause & INT_RX) - rxq_process(mp->rxq, INT_MAX); + if (int_cause & INT_RX) { + int i; + + for (i = 7; i >= 0; i--) + if (mp->rxq_mask & (1 << i)) + rxq_process(mp->rxq + i, INT_MAX); + } #endif if (int_cause_ext & INT_EXT_TX) { @@ -1707,13 +1726,16 @@ static void port_start(struct mv643xx_eth_private *mp) wrl(mp, PORT_CONFIG_EXT(mp->port_num), 0x00000000); /* - * Enable the receive queue. + * Enable the receive queues. */ - for (i = 0; i < 1; i++) { - struct rx_queue *rxq = mp->rxq; - int off = RXQ_CURRENT_DESC_PTR(mp->port_num); + for (i = 0; i < 8; i++) { + struct rx_queue *rxq = mp->rxq + i; + int off = RXQ_CURRENT_DESC_PTR(mp->port_num, i); u32 addr; + if ((mp->rxq_mask & (1 << i)) == 0) + continue; + addr = (u32)rxq->rx_desc_dma; addr += rxq->rx_curr_desc * sizeof(struct rx_desc); wrl(mp, off, addr); @@ -1748,6 +1770,7 @@ static int mv643xx_eth_open(struct net_device *dev) { struct mv643xx_eth_private *mp = netdev_priv(dev); int err; + int i; wrl(mp, INT_CAUSE(mp->port_num), 0); wrl(mp, INT_CAUSE_EXT(mp->port_num), 0); @@ -1763,10 +1786,20 @@ static int mv643xx_eth_open(struct net_device *dev) init_mac_tables(mp); - err = rxq_init(mp); - if (err) - goto out; - rxq_refill(mp->rxq); + for (i = 0; i < 8; i++) { + if ((mp->rxq_mask & (1 << i)) == 0) + continue; + + err = rxq_init(mp, i); + if (err) { + while (--i >= 0) + if (mp->rxq_mask & (1 << i)) + rxq_deinit(mp->rxq + i); + goto out; + } + + rxq_refill(mp->rxq + i); + } err = txq_init(mp); if (err) @@ -1790,7 +1823,9 @@ static int mv643xx_eth_open(struct net_device *dev) out_free: - rxq_deinit(mp->rxq); + for (i = 0; i < 8; i++) + if (mp->rxq_mask & (1 << i)) + rxq_deinit(mp->rxq + i); out: free_irq(dev->irq, dev); @@ -1800,9 +1835,13 @@ out: static void port_reset(struct mv643xx_eth_private *mp) { unsigned int data; + int i; + for (i = 0; i < 8; i++) { + if (mp->rxq_mask & (1 << i)) + rxq_disable(mp->rxq + i); + } txq_disable(mp->txq); - rxq_disable(mp->rxq); while (!(rdl(mp, PORT_STATUS(mp->port_num)) & TX_FIFO_EMPTY)) udelay(10); @@ -1817,6 +1856,7 @@ static void port_reset(struct mv643xx_eth_private *mp) static int mv643xx_eth_stop(struct net_device *dev) { struct mv643xx_eth_private *mp = netdev_priv(dev); + int i; wrl(mp, INT_MASK(mp->port_num), 0x00000000); rdl(mp, INT_MASK(mp->port_num)); @@ -1832,8 +1872,11 @@ static int mv643xx_eth_stop(struct net_device *dev) port_reset(mp); mib_counters_update(mp); + for (i = 0; i < 8; i++) { + if (mp->rxq_mask & (1 << i)) + rxq_deinit(mp->rxq + i); + } txq_deinit(mp->txq); - rxq_deinit(mp->rxq); return 0; } @@ -2085,6 +2128,12 @@ static void set_params(struct mv643xx_eth_private *mp, mp->rx_desc_sram_addr = pd->rx_sram_addr; mp->rx_desc_sram_size = pd->rx_sram_size; + if (pd->rx_queue_mask) + mp->rxq_mask = pd->rx_queue_mask; + else + mp->rxq_mask = 0x01; + mp->rxq_primary = fls(mp->rxq_mask) - 1; + mp->default_tx_ring_size = DEFAULT_TX_QUEUE_SIZE; if (pd->tx_queue_size) mp->default_tx_ring_size = pd->tx_queue_size; diff --git a/include/linux/mv643xx_eth.h b/include/linux/mv643xx_eth.h index 646177660495..1afd7ba6d303 100644 --- a/include/linux/mv643xx_eth.h +++ b/include/linux/mv643xx_eth.h @@ -48,6 +48,11 @@ struct mv643xx_eth_platform_data { int speed; int duplex; + /* + * Which RX queues to use. + */ + int rx_queue_mask; + /* * Override default RX/TX queue sizes if nonzero. */ -- cgit v1.2.3 From 3d6b35bc5090cf8d8b7e62eca1f9c21ca56fc6c7 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 2 Jun 2008 01:28:22 +0200 Subject: mv643xx_eth: allow multiple TX queues As with the multiple RX queue support, allow the platform code to specify that the hardware we are running on supports multiple TX queues. This patch only uses the highest-numbered enabled queue to send packets to for now, this can be extended later to enable QoS and such. Signed-off-by: Lennert Buytenhek Acked-by: Dale Farnsworth --- drivers/net/mv643xx_eth.c | 146 +++++++++++++++++++++++++++++++------------- include/linux/mv643xx_eth.h | 3 +- 2 files changed, 105 insertions(+), 44 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index 3c8591853999..287155ea9ce1 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -103,16 +103,16 @@ static char mv643xx_eth_driver_version[] = "1.0"; #define INT_EXT_PHY 0x00010000 #define INT_EXT_TX_ERROR_0 0x00000100 #define INT_EXT_TX_0 0x00000001 -#define INT_EXT_TX 0x00000101 +#define INT_EXT_TX 0x0000ffff #define INT_MASK(p) (0x0468 + ((p) << 10)) #define INT_MASK_EXT(p) (0x046c + ((p) << 10)) #define TX_FIFO_URGENT_THRESHOLD(p) (0x0474 + ((p) << 10)) #define RXQ_CURRENT_DESC_PTR(p, q) (0x060c + ((p) << 10) + ((q) << 4)) #define RXQ_COMMAND(p) (0x0680 + ((p) << 10)) -#define TXQ_CURRENT_DESC_PTR(p) (0x06c0 + ((p) << 10)) -#define TXQ_BW_TOKENS(p) (0x0700 + ((p) << 10)) -#define TXQ_BW_CONF(p) (0x0704 + ((p) << 10)) -#define TXQ_BW_WRR_CONF(p) (0x0708 + ((p) << 10)) +#define TXQ_CURRENT_DESC_PTR(p, q) (0x06c0 + ((p) << 10) + ((q) << 2)) +#define TXQ_BW_TOKENS(p, q) (0x0700 + ((p) << 10) + ((q) << 4)) +#define TXQ_BW_CONF(p, q) (0x0704 + ((p) << 10) + ((q) << 4)) +#define TXQ_BW_WRR_CONF(p, q) (0x0708 + ((p) << 10) + ((q) << 4)) #define MIB_COUNTERS(p) (0x1000 + ((p) << 7)) #define SPECIAL_MCAST_TABLE(p) (0x1400 + ((p) << 10)) #define OTHER_MCAST_TABLE(p) (0x1500 + ((p) << 10)) @@ -303,6 +303,8 @@ struct rx_queue { }; struct tx_queue { + int index; + int tx_ring_size; int tx_desc_count; @@ -347,7 +349,9 @@ struct mv643xx_eth_private { int default_tx_ring_size; unsigned long tx_desc_sram_addr; int tx_desc_sram_size; - struct tx_queue txq[1]; + u8 txq_mask; + int txq_primary; + struct tx_queue txq[8]; #ifdef MV643XX_ETH_TX_FAST_REFILL int tx_clean_threshold; #endif @@ -374,7 +378,7 @@ static struct mv643xx_eth_private *rxq_to_mp(struct rx_queue *rxq) static struct mv643xx_eth_private *txq_to_mp(struct tx_queue *txq) { - return container_of(txq, struct mv643xx_eth_private, txq[0]); + return container_of(txq, struct mv643xx_eth_private, txq[txq->index]); } static void rxq_enable(struct rx_queue *rxq) @@ -396,13 +400,13 @@ static void rxq_disable(struct rx_queue *rxq) static void txq_enable(struct tx_queue *txq) { struct mv643xx_eth_private *mp = txq_to_mp(txq); - wrl(mp, TXQ_COMMAND(mp->port_num), 1); + wrl(mp, TXQ_COMMAND(mp->port_num), 1 << txq->index); } static void txq_disable(struct tx_queue *txq) { struct mv643xx_eth_private *mp = txq_to_mp(txq); - u8 mask = 1; + u8 mask = 1 << txq->index; wrl(mp, TXQ_COMMAND(mp->port_num), mask << 8); while (rdl(mp, TXQ_COMMAND(mp->port_num)) & mask) @@ -413,6 +417,12 @@ static void __txq_maybe_wake(struct tx_queue *txq) { struct mv643xx_eth_private *mp = txq_to_mp(txq); + /* + * netif_{stop,wake}_queue() flow control only applies to + * the primary queue. + */ + BUG_ON(txq->index != mp->txq_primary); + if (txq->tx_ring_size - txq->tx_desc_count >= MAX_DESCS_PER_SKB) netif_wake_queue(mp->dev); } @@ -593,8 +603,10 @@ static int mv643xx_eth_poll(struct napi_struct *napi, int budget) #ifdef MV643XX_ETH_TX_FAST_REFILL if (++mp->tx_clean_threshold > 5) { - txq_reclaim(mp->txq, 0); mp->tx_clean_threshold = 0; + for (i = 0; i < 8; i++) + if (mp->txq_mask & (1 << i)) + txq_reclaim(mp->txq + i, 0); } #endif @@ -754,8 +766,6 @@ static int mv643xx_eth_xmit(struct sk_buff *skb, struct net_device *dev) struct tx_queue *txq; unsigned long flags; - BUG_ON(netif_queue_stopped(dev)); - if (has_tiny_unaligned_frags(skb) && __skb_linearize(skb)) { stats->tx_dropped++; dev_printk(KERN_DEBUG, &dev->dev, @@ -766,13 +776,15 @@ static int mv643xx_eth_xmit(struct sk_buff *skb, struct net_device *dev) spin_lock_irqsave(&mp->lock, flags); - txq = mp->txq; + txq = mp->txq + mp->txq_primary; if (txq->tx_ring_size - txq->tx_desc_count < MAX_DESCS_PER_SKB) { - printk(KERN_ERR "%s: transmit with queue full\n", dev->name); - netif_stop_queue(dev); spin_unlock_irqrestore(&mp->lock, flags); - return NETDEV_TX_BUSY; + if (txq->index == mp->txq_primary && net_ratelimit()) + dev_printk(KERN_ERR, &dev->dev, + "primary tx queue full?!\n"); + kfree_skb(skb); + return NETDEV_TX_OK; } txq_submit_skb(txq, skb); @@ -780,8 +792,13 @@ static int mv643xx_eth_xmit(struct sk_buff *skb, struct net_device *dev) stats->tx_packets++; dev->trans_start = jiffies; - if (txq->tx_ring_size - txq->tx_desc_count < MAX_DESCS_PER_SKB) - netif_stop_queue(dev); + if (txq->index == mp->txq_primary) { + int entries_left; + + entries_left = txq->tx_ring_size - txq->tx_desc_count; + if (entries_left < MAX_DESCS_PER_SKB) + netif_stop_queue(dev); + } spin_unlock_irqrestore(&mp->lock, flags); @@ -831,8 +848,8 @@ static void txq_set_rate(struct tx_queue *txq, int rate, int burst) if (bucket_size > 65535) bucket_size = 65535; - wrl(mp, TXQ_BW_TOKENS(mp->port_num), token_rate << 14); - wrl(mp, TXQ_BW_CONF(mp->port_num), + wrl(mp, TXQ_BW_TOKENS(mp->port_num, txq->index), token_rate << 14); + wrl(mp, TXQ_BW_CONF(mp->port_num, txq->index), (bucket_size << 10) | token_rate); } @@ -848,7 +865,7 @@ static void txq_set_fixed_prio_mode(struct tx_queue *txq) off = TXQ_FIX_PRIO_CONF(mp->port_num); val = rdl(mp, off); - val |= 1; + val |= 1 << txq->index; wrl(mp, off, val); } @@ -864,13 +881,13 @@ static void txq_set_wrr(struct tx_queue *txq, int weight) off = TXQ_FIX_PRIO_CONF(mp->port_num); val = rdl(mp, off); - val &= ~1; + val &= ~(1 << txq->index); wrl(mp, off, val); /* * Configure WRR weight for this queue. */ - off = TXQ_BW_WRR_CONF(mp->port_num); + off = TXQ_BW_WRR_CONF(mp->port_num, txq->index); val = rdl(mp, off); val = (val & ~0xff) | (weight & 0xff); @@ -1415,13 +1432,15 @@ static void rxq_deinit(struct rx_queue *rxq) kfree(rxq->rx_skb); } -static int txq_init(struct mv643xx_eth_private *mp) +static int txq_init(struct mv643xx_eth_private *mp, int index) { - struct tx_queue *txq = mp->txq; + struct tx_queue *txq = mp->txq + index; struct tx_desc *tx_desc; int size; int i; + txq->index = index; + txq->tx_ring_size = mp->default_tx_ring_size; txq->tx_desc_count = 0; @@ -1430,7 +1449,7 @@ static int txq_init(struct mv643xx_eth_private *mp) size = txq->tx_ring_size * sizeof(struct tx_desc); - if (size <= mp->tx_desc_sram_size) { + if (index == mp->txq_primary && size <= mp->tx_desc_sram_size) { txq->tx_desc_area = ioremap(mp->tx_desc_sram_addr, mp->tx_desc_sram_size); txq->tx_desc_dma = mp->tx_desc_sram_addr; @@ -1467,7 +1486,7 @@ static int txq_init(struct mv643xx_eth_private *mp) out_free: - if (size <= mp->tx_desc_sram_size) + if (index == mp->txq_primary && size <= mp->tx_desc_sram_size) iounmap(txq->tx_desc_area); else dma_free_coherent(NULL, size, @@ -1539,7 +1558,8 @@ static void txq_deinit(struct tx_queue *txq) BUG_ON(txq->tx_used_desc != txq->tx_curr_desc); - if (txq->tx_desc_area_size <= mp->tx_desc_sram_size) + if (txq->index == mp->txq_primary && + txq->tx_desc_area_size <= mp->tx_desc_sram_size) iounmap(txq->tx_desc_area); else dma_free_coherent(NULL, txq->tx_desc_area_size, @@ -1578,12 +1598,20 @@ static void update_pscr(struct mv643xx_eth_private *mp, int speed, int duplex) if ((pscr_o & SERIAL_PORT_ENABLE) == 0) wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr_n); else { - txq_disable(mp->txq); + int i; + + for (i = 0; i < 8; i++) + if (mp->txq_mask & (1 << i)) + txq_disable(mp->txq + i); + pscr_o &= ~SERIAL_PORT_ENABLE; wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr_o); wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr_n); wrl(mp, PORT_SERIAL_CONTROL(mp->port_num), pscr_n); - txq_enable(mp->txq); + + for (i = 0; i < 8; i++) + if (mp->txq_mask & (1 << i)) + txq_enable(mp->txq + i); } } } @@ -1609,13 +1637,17 @@ static irqreturn_t mv643xx_eth_irq(int irq, void *dev_id) if (int_cause_ext & (INT_EXT_PHY | INT_EXT_LINK)) { if (mii_link_ok(&mp->mii)) { struct ethtool_cmd cmd; + int i; mii_ethtool_gset(&mp->mii, &cmd); update_pscr(mp, cmd.speed, cmd.duplex); - txq_enable(mp->txq); + for (i = 0; i < 8; i++) + if (mp->txq_mask & (1 << i)) + txq_enable(mp->txq + i); + if (!netif_carrier_ok(dev)) { netif_carrier_on(dev); - __txq_maybe_wake(mp->txq); + __txq_maybe_wake(mp->txq + mp->txq_primary); } } else if (netif_carrier_ok(dev)) { netif_stop_queue(dev); @@ -1643,9 +1675,17 @@ static irqreturn_t mv643xx_eth_irq(int irq, void *dev_id) } #endif + /* + * TxBuffer or TxError set for any of the 8 queues? + */ if (int_cause_ext & INT_EXT_TX) { - txq_reclaim(mp->txq, 0); - __txq_maybe_wake(mp->txq); + int i; + + for (i = 0; i < 8; i++) + if (mp->txq_mask & (1 << i)) + txq_reclaim(mp->txq + i, 0); + + __txq_maybe_wake(mp->txq + mp->txq_primary); } return IRQ_HANDLED; @@ -1696,11 +1736,14 @@ static void port_start(struct mv643xx_eth_private *mp) * Configure TX path and queues. */ tx_set_rate(mp, 1000000000, 16777216); - for (i = 0; i < 1; i++) { - struct tx_queue *txq = mp->txq; - int off = TXQ_CURRENT_DESC_PTR(mp->port_num); + for (i = 0; i < 8; i++) { + struct tx_queue *txq = mp->txq + i; + int off = TXQ_CURRENT_DESC_PTR(mp->port_num, i); u32 addr; + if ((mp->txq_mask & (1 << i)) == 0) + continue; + addr = (u32)txq->tx_desc_dma; addr += txq->tx_curr_desc * sizeof(struct tx_desc); wrl(mp, off, addr); @@ -1801,9 +1844,18 @@ static int mv643xx_eth_open(struct net_device *dev) rxq_refill(mp->rxq + i); } - err = txq_init(mp); - if (err) - goto out_free; + for (i = 0; i < 8; i++) { + if ((mp->txq_mask & (1 << i)) == 0) + continue; + + err = txq_init(mp, i); + if (err) { + while (--i >= 0) + if (mp->txq_mask & (1 << i)) + txq_deinit(mp->txq + i); + goto out_free; + } + } #ifdef MV643XX_ETH_NAPI napi_enable(&mp->napi); @@ -1840,8 +1892,9 @@ static void port_reset(struct mv643xx_eth_private *mp) for (i = 0; i < 8; i++) { if (mp->rxq_mask & (1 << i)) rxq_disable(mp->rxq + i); + if (mp->txq_mask & (1 << i)) + txq_disable(mp->txq + i); } - txq_disable(mp->txq); while (!(rdl(mp, PORT_STATUS(mp->port_num)) & TX_FIFO_EMPTY)) udelay(10); @@ -1875,8 +1928,9 @@ static int mv643xx_eth_stop(struct net_device *dev) for (i = 0; i < 8; i++) { if (mp->rxq_mask & (1 << i)) rxq_deinit(mp->rxq + i); + if (mp->txq_mask & (1 << i)) + txq_deinit(mp->txq + i); } - txq_deinit(mp->txq); return 0; } @@ -1928,7 +1982,7 @@ static void tx_timeout_task(struct work_struct *ugly) port_reset(mp); port_start(mp); - __txq_maybe_wake(mp->txq); + __txq_maybe_wake(mp->txq + mp->txq_primary); } } @@ -2139,6 +2193,12 @@ static void set_params(struct mv643xx_eth_private *mp, mp->default_tx_ring_size = pd->tx_queue_size; mp->tx_desc_sram_addr = pd->tx_sram_addr; mp->tx_desc_sram_size = pd->tx_sram_size; + + if (pd->tx_queue_mask) + mp->txq_mask = pd->tx_queue_mask; + else + mp->txq_mask = 0x01; + mp->txq_primary = fls(mp->txq_mask) - 1; } static int phy_detect(struct mv643xx_eth_private *mp) diff --git a/include/linux/mv643xx_eth.h b/include/linux/mv643xx_eth.h index 1afd7ba6d303..12078577aef6 100644 --- a/include/linux/mv643xx_eth.h +++ b/include/linux/mv643xx_eth.h @@ -49,9 +49,10 @@ struct mv643xx_eth_platform_data { int duplex; /* - * Which RX queues to use. + * Which RX/TX queues to use. */ int rx_queue_mask; + int tx_queue_mask; /* * Override default RX/TX queue sizes if nonzero. -- cgit v1.2.3 From d8f3de0d2412bb91639cfefc5b3c79dbf3812212 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 12 Jun 2008 23:24:06 +0200 Subject: Suspend-related patches for 2.6.27 ACPI PM: Add possibility to change suspend sequence There are some systems out there that don't work correctly with our current suspend/hibernation code ordering. Provide a workaround for these systems allowing them to pass 'acpi_sleep=old_ordering' in the kernel command line so that it will use the pre-ACPI 2.0 ("old") suspend code ordering. Unfortunately, this requires us to add a platform hook to the resuming of devices for recovering the platform in case one of the device drivers' .suspend() routines returns error code. Namely, ACPI 1.0 specifies that _PTS should be called before suspending devices, but _WAK still should be called before resuming them in order to undo the changes made by _PTS. However, if there is an error during suspending devices, they are automatically resumed without returning control to the PM core, so the _WAK has to be called from within device_resume() in that cases. The patch also reorders and refactors the ACPI suspend/hibernation code to avoid duplication as far as reasonably possible. Signed-off-by: Rafael J. Wysocki Acked-by: Pavel Machek Signed-off-by: Jesse Barnes --- Documentation/kernel-parameters.txt | 6 +- arch/x86/kernel/acpi/sleep.c | 2 + drivers/acpi/sleep/main.c | 276 +++++++++++++++++++++--------------- drivers/base/power/main.c | 2 - include/linux/acpi.h | 3 + include/linux/suspend.h | 14 +- kernel/power/disk.c | 28 +++- kernel/power/main.c | 10 +- 8 files changed, 215 insertions(+), 126 deletions(-) (limited to 'include/linux') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 9cf7b34f2db0..18d793ea0dd3 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -147,10 +147,14 @@ and is between 256 and 4096 characters. It is defined in the file default: 0 acpi_sleep= [HW,ACPI] Sleep options - Format: { s3_bios, s3_mode, s3_beep } + Format: { s3_bios, s3_mode, s3_beep, old_ordering } See Documentation/power/video.txt for s3_bios and s3_mode. s3_beep is for debugging; it makes the PC's speaker beep as soon as the kernel's real-mode entry point is called. + old_ordering causes the ACPI 1.0 ordering of the _PTS + control method, wrt putting devices into low power + states, to be enforced (the ACPI 2.0 ordering of _PTS is + used by default). acpi_sci= [HW,ACPI] ACPI System Control Interrupt trigger mode Format: { level | edge | high | low } diff --git a/arch/x86/kernel/acpi/sleep.c b/arch/x86/kernel/acpi/sleep.c index afc25ee9964b..882e970032d5 100644 --- a/arch/x86/kernel/acpi/sleep.c +++ b/arch/x86/kernel/acpi/sleep.c @@ -124,6 +124,8 @@ static int __init acpi_sleep_setup(char *str) acpi_realmode_flags |= 2; if (strncmp(str, "s3_beep", 7) == 0) acpi_realmode_flags |= 4; + if (strncmp(str, "old_ordering", 12) == 0) + acpi_old_suspend_ordering(); str = strchr(str, ','); if (str != NULL) str += strspn(str, ", \t"); diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c index 0f2caea2fc83..4addf8ad50ae 100644 --- a/drivers/acpi/sleep/main.c +++ b/drivers/acpi/sleep/main.c @@ -24,10 +24,6 @@ u8 sleep_states[ACPI_S_STATE_COUNT]; -#ifdef CONFIG_PM_SLEEP -static u32 acpi_target_sleep_state = ACPI_STATE_S0; -#endif - static int acpi_sleep_prepare(u32 acpi_state) { #ifdef CONFIG_ACPI_SLEEP @@ -50,9 +46,96 @@ static int acpi_sleep_prepare(u32 acpi_state) return 0; } -#ifdef CONFIG_SUSPEND -static struct platform_suspend_ops acpi_suspend_ops; +#ifdef CONFIG_PM_SLEEP +static u32 acpi_target_sleep_state = ACPI_STATE_S0; + +/* + * ACPI 1.0 wants us to execute _PTS before suspending devices, so we allow the + * user to request that behavior by using the 'acpi_old_suspend_ordering' + * kernel command line option that causes the following variable to be set. + */ +static bool old_suspend_ordering; + +void __init acpi_old_suspend_ordering(void) +{ + old_suspend_ordering = true; +} + +/** + * acpi_pm_disable_gpes - Disable the GPEs. + */ +static int acpi_pm_disable_gpes(void) +{ + acpi_hw_disable_all_gpes(); + return 0; +} + +/** + * __acpi_pm_prepare - Prepare the platform to enter the target state. + * + * If necessary, set the firmware waking vector and do arch-specific + * nastiness to get the wakeup code to the waking vector. + */ +static int __acpi_pm_prepare(void) +{ + int error = acpi_sleep_prepare(acpi_target_sleep_state); + + if (error) + acpi_target_sleep_state = ACPI_STATE_S0; + return error; +} + +/** + * acpi_pm_prepare - Prepare the platform to enter the target sleep + * state and disable the GPEs. + */ +static int acpi_pm_prepare(void) +{ + int error = __acpi_pm_prepare(); + + if (!error) + acpi_hw_disable_all_gpes(); + return error; +} + +/** + * acpi_pm_finish - Instruct the platform to leave a sleep state. + * + * This is called after we wake back up (or if entering the sleep state + * failed). + */ +static void acpi_pm_finish(void) +{ + u32 acpi_state = acpi_target_sleep_state; + + if (acpi_state == ACPI_STATE_S0) + return; + printk(KERN_INFO PREFIX "Waking up from system sleep state S%d\n", + acpi_state); + acpi_disable_wakeup_device(acpi_state); + acpi_leave_sleep_state(acpi_state); + + /* reset firmware waking vector */ + acpi_set_firmware_waking_vector((acpi_physical_address) 0); + + acpi_target_sleep_state = ACPI_STATE_S0; +} + +/** + * acpi_pm_end - Finish up suspend sequence. + */ +static void acpi_pm_end(void) +{ + /* + * This is necessary in case acpi_pm_finish() is not called during a + * failing transition to a sleep state. + */ + acpi_target_sleep_state = ACPI_STATE_S0; +} +#endif /* CONFIG_PM_SLEEP */ + +#ifdef CONFIG_SUSPEND extern void do_suspend_lowlevel(void); static u32 acpi_suspend_states[] = { @@ -66,7 +149,6 @@ static u32 acpi_suspend_states[] = { * acpi_suspend_begin - Set the target system sleep state to the state * associated with given @pm_state, if supported. */ - static int acpi_suspend_begin(suspend_state_t pm_state) { u32 acpi_state = acpi_suspend_states[pm_state]; @@ -82,25 +164,6 @@ static int acpi_suspend_begin(suspend_state_t pm_state) return error; } -/** - * acpi_suspend_prepare - Do preliminary suspend work. - * - * If necessary, set the firmware waking vector and do arch-specific - * nastiness to get the wakeup code to the waking vector. - */ - -static int acpi_suspend_prepare(void) -{ - int error = acpi_sleep_prepare(acpi_target_sleep_state); - - if (error) { - acpi_target_sleep_state = ACPI_STATE_S0; - return error; - } - - return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT; -} - /** * acpi_suspend_enter - Actually enter a sleep state. * @pm_state: ignored @@ -109,7 +172,6 @@ static int acpi_suspend_prepare(void) * assembly, which in turn call acpi_enter_sleep_state(). * It's unfortunate, but it works. Please fix if you're feeling frisky. */ - static int acpi_suspend_enter(suspend_state_t pm_state) { acpi_status status = AE_OK; @@ -166,39 +228,6 @@ static int acpi_suspend_enter(suspend_state_t pm_state) return ACPI_SUCCESS(status) ? 0 : -EFAULT; } -/** - * acpi_suspend_finish - Instruct the platform to leave a sleep state. - * - * This is called after we wake back up (or if entering the sleep state - * failed). - */ - -static void acpi_suspend_finish(void) -{ - u32 acpi_state = acpi_target_sleep_state; - - acpi_disable_wakeup_device(acpi_state); - acpi_leave_sleep_state(acpi_state); - - /* reset firmware waking vector */ - acpi_set_firmware_waking_vector((acpi_physical_address) 0); - - acpi_target_sleep_state = ACPI_STATE_S0; -} - -/** - * acpi_suspend_end - Finish up suspend sequence. - */ - -static void acpi_suspend_end(void) -{ - /* - * This is necessary in case acpi_suspend_finish() is not called during a - * failing transition to a sleep state. - */ - acpi_target_sleep_state = ACPI_STATE_S0; -} - static int acpi_suspend_state_valid(suspend_state_t pm_state) { u32 acpi_state; @@ -218,10 +247,39 @@ static int acpi_suspend_state_valid(suspend_state_t pm_state) static struct platform_suspend_ops acpi_suspend_ops = { .valid = acpi_suspend_state_valid, .begin = acpi_suspend_begin, - .prepare = acpi_suspend_prepare, + .prepare = acpi_pm_prepare, + .enter = acpi_suspend_enter, + .finish = acpi_pm_finish, + .end = acpi_pm_end, +}; + +/** + * acpi_suspend_begin_old - Set the target system sleep state to the + * state associated with given @pm_state, if supported, and + * execute the _PTS control method. This function is used if the + * pre-ACPI 2.0 suspend ordering has been requested. + */ +static int acpi_suspend_begin_old(suspend_state_t pm_state) +{ + int error = acpi_suspend_begin(pm_state); + + if (!error) + error = __acpi_pm_prepare(); + return error; +} + +/* + * The following callbacks are used if the pre-ACPI 2.0 suspend ordering has + * been requested. + */ +static struct platform_suspend_ops acpi_suspend_ops_old = { + .valid = acpi_suspend_state_valid, + .begin = acpi_suspend_begin_old, + .prepare = acpi_pm_disable_gpes, .enter = acpi_suspend_enter, - .finish = acpi_suspend_finish, - .end = acpi_suspend_end, + .finish = acpi_pm_finish, + .end = acpi_pm_end, + .recover = acpi_pm_finish, }; #endif /* CONFIG_SUSPEND */ @@ -229,22 +287,9 @@ static struct platform_suspend_ops acpi_suspend_ops = { static int acpi_hibernation_begin(void) { acpi_target_sleep_state = ACPI_STATE_S4; - return 0; } -static int acpi_hibernation_prepare(void) -{ - int error = acpi_sleep_prepare(ACPI_STATE_S4); - - if (error) { - acpi_target_sleep_state = ACPI_STATE_S0; - return error; - } - - return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT; -} - static int acpi_hibernation_enter(void) { acpi_status status = AE_OK; @@ -274,52 +319,55 @@ static void acpi_hibernation_leave(void) acpi_leave_sleep_state_prep(ACPI_STATE_S4); } -static void acpi_hibernation_finish(void) +static void acpi_pm_enable_gpes(void) { - acpi_disable_wakeup_device(ACPI_STATE_S4); - acpi_leave_sleep_state(ACPI_STATE_S4); - - /* reset firmware waking vector */ - acpi_set_firmware_waking_vector((acpi_physical_address) 0); - - acpi_target_sleep_state = ACPI_STATE_S0; + acpi_hw_enable_all_runtime_gpes(); } -static void acpi_hibernation_end(void) -{ - /* - * This is necessary in case acpi_hibernation_finish() is not called - * during a failing transition to the sleep state. - */ - acpi_target_sleep_state = ACPI_STATE_S0; -} +static struct platform_hibernation_ops acpi_hibernation_ops = { + .begin = acpi_hibernation_begin, + .end = acpi_pm_end, + .pre_snapshot = acpi_pm_prepare, + .finish = acpi_pm_finish, + .prepare = acpi_pm_prepare, + .enter = acpi_hibernation_enter, + .leave = acpi_hibernation_leave, + .pre_restore = acpi_pm_disable_gpes, + .restore_cleanup = acpi_pm_enable_gpes, +}; -static int acpi_hibernation_pre_restore(void) +/** + * acpi_hibernation_begin_old - Set the target system sleep state to + * ACPI_STATE_S4 and execute the _PTS control method. This + * function is used if the pre-ACPI 2.0 suspend ordering has been + * requested. + */ +static int acpi_hibernation_begin_old(void) { - acpi_status status; - - status = acpi_hw_disable_all_gpes(); - - return ACPI_SUCCESS(status) ? 0 : -EFAULT; -} + int error = acpi_sleep_prepare(ACPI_STATE_S4); -static void acpi_hibernation_restore_cleanup(void) -{ - acpi_hw_enable_all_runtime_gpes(); + if (!error) + acpi_target_sleep_state = ACPI_STATE_S4; + return error; } -static struct platform_hibernation_ops acpi_hibernation_ops = { - .begin = acpi_hibernation_begin, - .end = acpi_hibernation_end, - .pre_snapshot = acpi_hibernation_prepare, - .finish = acpi_hibernation_finish, - .prepare = acpi_hibernation_prepare, +/* + * The following callbacks are used if the pre-ACPI 2.0 suspend ordering has + * been requested. + */ +static struct platform_hibernation_ops acpi_hibernation_ops_old = { + .begin = acpi_hibernation_begin_old, + .end = acpi_pm_end, + .pre_snapshot = acpi_pm_disable_gpes, + .finish = acpi_pm_finish, + .prepare = acpi_pm_disable_gpes, .enter = acpi_hibernation_enter, .leave = acpi_hibernation_leave, - .pre_restore = acpi_hibernation_pre_restore, - .restore_cleanup = acpi_hibernation_restore_cleanup, + .pre_restore = acpi_pm_disable_gpes, + .restore_cleanup = acpi_pm_enable_gpes, + .recover = acpi_pm_finish, }; -#endif /* CONFIG_HIBERNATION */ +#endif /* CONFIG_HIBERNATION */ int acpi_suspend(u32 acpi_state) { @@ -461,13 +509,15 @@ int __init acpi_sleep_init(void) } } - suspend_set_ops(&acpi_suspend_ops); + suspend_set_ops(old_suspend_ordering ? + &acpi_suspend_ops_old : &acpi_suspend_ops); #endif #ifdef CONFIG_HIBERNATION status = acpi_get_sleep_type_data(ACPI_STATE_S4, &type_a, &type_b); if (ACPI_SUCCESS(status)) { - hibernation_set_ops(&acpi_hibernation_ops); + hibernation_set_ops(old_suspend_ordering ? + &acpi_hibernation_ops_old : &acpi_hibernation_ops); sleep_states[ACPI_STATE_S4] = 1; printk(" S4"); } diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index d571204aaff7..3250c5257b74 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -781,8 +781,6 @@ int device_suspend(pm_message_t state) error = dpm_prepare(state); if (!error) error = dpm_suspend(state); - if (error) - device_resume(resume_event(state)); return error; } EXPORT_SYMBOL_GPL(device_suspend); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 41f7ce7edd7a..33adcf91ef41 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -234,6 +234,9 @@ int acpi_check_region(resource_size_t start, resource_size_t n, int acpi_check_mem_region(resource_size_t start, resource_size_t n, const char *name); +#ifdef CONFIG_PM_SLEEP +void __init acpi_old_suspend_ordering(void); +#endif /* CONFIG_PM_SLEEP */ #else /* CONFIG_ACPI */ static inline int early_acpi_boot_init(void) diff --git a/include/linux/suspend.h b/include/linux/suspend.h index a6977423baf7..e8e69159af71 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -86,6 +86,11 @@ typedef int __bitwise suspend_state_t; * that implement @begin(), but platforms implementing @begin() should * also provide a @end() which cleans up transitions aborted before * @enter(). + * + * @recover: Recover the platform from a suspend failure. + * Called by the PM core if the suspending of devices fails. + * This callback is optional and should only be implemented by platforms + * which require special recovery actions in that situation. */ struct platform_suspend_ops { int (*valid)(suspend_state_t state); @@ -94,6 +99,7 @@ struct platform_suspend_ops { int (*enter)(suspend_state_t state); void (*finish)(void); void (*end)(void); + void (*recover)(void); }; #ifdef CONFIG_SUSPEND @@ -149,7 +155,7 @@ extern void mark_free_pages(struct zone *zone); * The methods in this structure allow a platform to carry out special * operations required by it during a hibernation transition. * - * All the methods below must be implemented. + * All the methods below, except for @recover(), must be implemented. * * @begin: Tell the platform driver that we're starting hibernation. * Called right after shrinking memory and before freezing devices. @@ -189,6 +195,11 @@ extern void mark_free_pages(struct zone *zone); * @restore_cleanup: Clean up after a failing image restoration. * Called right after the nonboot CPUs have been enabled and before * thawing devices (runs with IRQs on). + * + * @recover: Recover the platform from a failure to suspend devices. + * Called by the PM core if the suspending of devices during hibernation + * fails. This callback is optional and should only be implemented by + * platforms which require special recovery actions in that situation. */ struct platform_hibernation_ops { int (*begin)(void); @@ -200,6 +211,7 @@ struct platform_hibernation_ops { void (*leave)(void); int (*pre_restore)(void); void (*restore_cleanup)(void); + void (*recover)(void); }; #ifdef CONFIG_HIBERNATION diff --git a/kernel/power/disk.c b/kernel/power/disk.c index d416be0efa8a..f011e0870b52 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -179,6 +179,17 @@ static void platform_restore_cleanup(int platform_mode) hibernation_ops->restore_cleanup(); } +/** + * platform_recover - recover the platform from a failure to suspend + * devices. + */ + +static void platform_recover(int platform_mode) +{ + if (platform_mode && hibernation_ops && hibernation_ops->recover) + hibernation_ops->recover(); +} + /** * create_image - freeze devices that need to be frozen with interrupts * off, create the hibernation image and thaw those devices. Control @@ -258,10 +269,10 @@ int hibernation_snapshot(int platform_mode) suspend_console(); error = device_suspend(PMSG_FREEZE); if (error) - goto Resume_console; + goto Recover_platform; if (hibernation_test(TEST_DEVICES)) - goto Resume_devices; + goto Recover_platform; error = platform_pre_snapshot(platform_mode); if (error || hibernation_test(TEST_PLATFORM)) @@ -285,11 +296,14 @@ int hibernation_snapshot(int platform_mode) Resume_devices: device_resume(in_suspend ? (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE); - Resume_console: resume_console(); Close: platform_end(platform_mode); return error; + + Recover_platform: + platform_recover(platform_mode); + goto Resume_devices; } /** @@ -398,8 +412,11 @@ int hibernation_platform_enter(void) suspend_console(); error = device_suspend(PMSG_HIBERNATE); - if (error) - goto Resume_console; + if (error) { + if (hibernation_ops->recover) + hibernation_ops->recover(); + goto Resume_devices; + } error = hibernation_ops->prepare(); if (error) @@ -428,7 +445,6 @@ int hibernation_platform_enter(void) hibernation_ops->finish(); Resume_devices: device_resume(PMSG_RESTORE); - Resume_console: resume_console(); Close: hibernation_ops->end(); diff --git a/kernel/power/main.c b/kernel/power/main.c index d023b6b584e5..3398f4651aa1 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -269,11 +269,11 @@ int suspend_devices_and_enter(suspend_state_t state) error = device_suspend(PMSG_SUSPEND); if (error) { printk(KERN_ERR "PM: Some devices failed to suspend\n"); - goto Resume_console; + goto Recover_platform; } if (suspend_test(TEST_DEVICES)) - goto Resume_devices; + goto Recover_platform; if (suspend_ops->prepare) { error = suspend_ops->prepare(); @@ -294,12 +294,16 @@ int suspend_devices_and_enter(suspend_state_t state) suspend_ops->finish(); Resume_devices: device_resume(PMSG_RESUME); - Resume_console: resume_console(); Close: if (suspend_ops->end) suspend_ops->end(); return error; + + Recover_platform: + if (suspend_ops->recover) + suspend_ops->recover(); + goto Resume_devices; } /** -- cgit v1.2.3 From 564d9bdd89df4ceece5d126ff3b7db506ae06548 Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Tue, 10 Jun 2008 14:04:14 +0200 Subject: nl80211: Fix comment merge error The comments ended up in the wrong place due to a merge error. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- include/linux/nl80211.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index ea6517e58b04..aa8411e2a160 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -122,13 +122,13 @@ enum nl80211_commands { NL80211_CMD_NEW_STATION, NL80211_CMD_DEL_STATION, - /* add commands here */ - NL80211_CMD_GET_MPATH, NL80211_CMD_SET_MPATH, NL80211_CMD_NEW_MPATH, NL80211_CMD_DEL_MPATH, + /* add commands here */ + /* used to define NL80211_CMD_MAX below */ __NL80211_CMD_AFTER_LAST, NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1 @@ -230,13 +230,13 @@ enum nl80211_attrs { NL80211_ATTR_MNTR_FLAGS, - /* add attributes here, update the policy in nl80211.c */ - NL80211_ATTR_MESH_ID, NL80211_ATTR_STA_PLINK_ACTION, NL80211_ATTR_MPATH_NEXT_HOP, NL80211_ATTR_MPATH_INFO, + /* add attributes here, update the policy in nl80211.c */ + __NL80211_ATTR_AFTER_LAST, NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1 }; -- cgit v1.2.3 From 963f55178b25cb673ab438edaae4127b1d014bc1 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 12 Jun 2008 09:47:00 +0800 Subject: iwlwifi: remove redundant flags regarding to FAT channel This patch removes redundant flags regarding to FAT channel. Use mac80211's flag instead. Signed-off-by: Emmanuel Grumbach Signed-off-by: Tomas Winkler Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-core.c | 42 +++++++++-------------------- drivers/net/wireless/iwlwifi/iwl-dev.h | 14 ---------- drivers/net/wireless/iwlwifi/iwl-eeprom.c | 19 ++++++++----- drivers/net/wireless/iwlwifi/iwl4965-base.c | 6 +++-- include/linux/ieee80211.h | 1 + 5 files changed, 31 insertions(+), 51 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 6c7617c1bb91..fad26f71d7ce 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -478,24 +478,7 @@ static int iwlcore_init_geos(struct iwl_priv *priv) if (ch->flags & EEPROM_CHANNEL_RADAR) geo_ch->flags |= IEEE80211_CHAN_RADAR; - switch (ch->fat_extension_channel) { - case HT_IE_EXT_CHANNEL_ABOVE: - /* only above is allowed, disable below */ - geo_ch->flags |= IEEE80211_CHAN_NO_FAT_BELOW; - break; - case HT_IE_EXT_CHANNEL_BELOW: - /* only below is allowed, disable above */ - geo_ch->flags |= IEEE80211_CHAN_NO_FAT_ABOVE; - break; - case HT_IE_EXT_CHANNEL_NONE: - /* fat not allowed: disable both*/ - geo_ch->flags |= (IEEE80211_CHAN_NO_FAT_ABOVE | - IEEE80211_CHAN_NO_FAT_BELOW); - break; - case HT_IE_EXT_CHANNEL_MAX: - /* both above and below are permitted */ - break; - } + geo_ch->flags |= ch->fat_extension_channel; if (ch->max_power_avg > priv->max_channel_txpower_limit) priv->max_channel_txpower_limit = @@ -507,7 +490,7 @@ static int iwlcore_init_geos(struct iwl_priv *priv) /* Save flags for reg domain usage */ geo_ch->orig_flags = geo_ch->flags; - IWL_DEBUG_INFO("Channel %d Freq=%d[%sGHz] %s flag=0%X\n", + IWL_DEBUG_INFO("Channel %d Freq=%d[%sGHz] %s flag=0x%X\n", ch->channel, geo_ch->center_freq, is_channel_a_band(ch) ? "5.2" : "2.4", geo_ch->flags & IEEE80211_CHAN_DISABLED ? @@ -552,6 +535,7 @@ static u8 is_single_rx_stream(struct iwl_priv *priv) (priv->current_ht_config.supp_mcs_set[2] == 0)) || priv->ps_mode == IWL_MIMO_PS_STATIC; } + static u8 iwl_is_channel_extension(struct iwl_priv *priv, enum ieee80211_band band, u16 channel, u8 extension_chan_offset) @@ -562,12 +546,12 @@ static u8 iwl_is_channel_extension(struct iwl_priv *priv, if (!is_channel_valid(ch_info)) return 0; - if (extension_chan_offset == IWL_EXT_CHANNEL_OFFSET_NONE) - return 0; - - if ((ch_info->fat_extension_channel == extension_chan_offset) || - (ch_info->fat_extension_channel == HT_IE_EXT_CHANNEL_MAX)) - return 1; + if (extension_chan_offset == IEEE80211_HT_IE_CHA_SEC_ABOVE) + return !(ch_info->fat_extension_channel & + IEEE80211_CHAN_NO_FAT_ABOVE); + else if (extension_chan_offset == IEEE80211_HT_IE_CHA_SEC_BELOW) + return !(ch_info->fat_extension_channel & + IEEE80211_CHAN_NO_FAT_BELOW); return 0; } @@ -579,7 +563,7 @@ u8 iwl_is_fat_tx_allowed(struct iwl_priv *priv, if ((!iwl_ht_conf->is_ht) || (iwl_ht_conf->supported_chan_width != IWL_CHANNEL_WIDTH_40MHZ) || - (iwl_ht_conf->extension_chan_offset == IWL_EXT_CHANNEL_OFFSET_NONE)) + (iwl_ht_conf->extension_chan_offset == IEEE80211_HT_IE_CHA_SEC_NONE)) return 0; if (sta_ht_inf) { @@ -619,13 +603,13 @@ void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info) /* Note: control channel is opposite of extension channel */ switch (ht_info->extension_chan_offset) { - case IWL_EXT_CHANNEL_OFFSET_ABOVE: + case IEEE80211_HT_IE_CHA_SEC_ABOVE: rxon->flags &= ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); break; - case IWL_EXT_CHANNEL_OFFSET_BELOW: + case IEEE80211_HT_IE_CHA_SEC_BELOW: rxon->flags |= RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; break; - case IWL_EXT_CHANNEL_OFFSET_NONE: + case IEEE80211_HT_IE_CHA_SEC_NONE: default: rxon->flags &= ~RXON_FLG_CHANNEL_MODE_MIXED_MSK; break; diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index fa35193e5a7a..daa14d9e5a80 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -179,15 +179,6 @@ struct iwl4965_scan_power_info { s8 requested_power; /* scan pwr (dBm) requested for chnl/rate */ }; -/* For fat_extension_channel */ -enum { - HT_IE_EXT_CHANNEL_NONE = 0, - HT_IE_EXT_CHANNEL_ABOVE, - HT_IE_EXT_CHANNEL_INVALID, - HT_IE_EXT_CHANNEL_BELOW, - HT_IE_EXT_CHANNEL_MAX -}; - /* * One for each channel, holds all channel setup data * Some of the fields (e.g. eeprom and flags/max_power_avg) are redundant @@ -782,11 +773,6 @@ struct iwl_kw { #define IWL_OPERATION_MODE_MIXED 2 #define IWL_OPERATION_MODE_20MHZ 3 -#define IWL_EXT_CHANNEL_OFFSET_NONE 0 -#define IWL_EXT_CHANNEL_OFFSET_ABOVE 1 -#define IWL_EXT_CHANNEL_OFFSET_RESERVE1 2 -#define IWL_EXT_CHANNEL_OFFSET_BELOW 3 - #define IWL_TX_CRC_SIZE 4 #define IWL_TX_DELIMITER_SIZE 4 diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.c b/drivers/net/wireless/iwlwifi/iwl-eeprom.c index 11f9d9557a0e..cbb812f9f620 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.c @@ -470,6 +470,11 @@ int iwl_init_channel_map(struct iwl_priv *priv) /* Copy the run-time flags so they are there even on * invalid channels */ ch_info->flags = eeprom_ch_info[ch].flags; + /* First write that fat is not enabled, and then enable + * one by one */ + ch_info->fat_extension_channel = + (IEEE80211_CHAN_NO_FAT_ABOVE | + IEEE80211_CHAN_NO_FAT_BELOW); if (!(is_channel_valid(ch_info))) { IWL_DEBUG_INFO("Ch. %d Flags %x [%sGHz] - " @@ -534,12 +539,14 @@ int iwl_init_channel_map(struct iwl_priv *priv) for (ch = 0; ch < eeprom_ch_count; ch++) { if ((band == 6) && - ((eeprom_ch_index[ch] == 5) || - (eeprom_ch_index[ch] == 6) || - (eeprom_ch_index[ch] == 7))) - fat_extension_chan = HT_IE_EXT_CHANNEL_MAX; + ((eeprom_ch_index[ch] == 5) || + (eeprom_ch_index[ch] == 6) || + (eeprom_ch_index[ch] == 7))) + /* both are allowed: above and below */ + fat_extension_chan = 0; else - fat_extension_chan = HT_IE_EXT_CHANNEL_ABOVE; + fat_extension_chan = + IEEE80211_CHAN_NO_FAT_BELOW; /* Set up driver's info for lower half */ iwl_set_fat_chan_info(priv, ieeeband, @@ -551,7 +558,7 @@ int iwl_init_channel_map(struct iwl_priv *priv) iwl_set_fat_chan_info(priv, ieeeband, (eeprom_ch_index[ch] + 4), &(eeprom_ch_info[ch]), - HT_IE_EXT_CHANNEL_BELOW); + IEEE80211_CHAN_NO_FAT_ABOVE); } } diff --git a/drivers/net/wireless/iwlwifi/iwl4965-base.c b/drivers/net/wireless/iwlwifi/iwl4965-base.c index 0acd42bd43c5..de40e893f09e 100644 --- a/drivers/net/wireless/iwlwifi/iwl4965-base.c +++ b/drivers/net/wireless/iwlwifi/iwl4965-base.c @@ -664,9 +664,11 @@ static void iwl4965_ht_conf(struct iwl_priv *priv, iwl_conf->extension_chan_offset = ht_bss_conf->bss_cap & IEEE80211_HT_IE_CHA_SEC_OFFSET; /* If no above or below channel supplied disable FAT channel */ - if (iwl_conf->extension_chan_offset != IWL_EXT_CHANNEL_OFFSET_ABOVE && - iwl_conf->extension_chan_offset != IWL_EXT_CHANNEL_OFFSET_BELOW) + if (iwl_conf->extension_chan_offset != IEEE80211_HT_IE_CHA_SEC_ABOVE && + iwl_conf->extension_chan_offset != IEEE80211_HT_IE_CHA_SEC_BELOW) { + iwl_conf->extension_chan_offset = IEEE80211_HT_IE_CHA_SEC_NONE; iwl_conf->supported_chan_width = 0; + } iwl_conf->tx_mimo_ps_mode = (u8)((ht_conf->cap & IEEE80211_HT_CAP_MIMO_PS) >> 2); diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 9300f37cd7e8..8f2c20b4a15d 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -320,6 +320,7 @@ struct ieee80211_ht_addt_info { #define IEEE80211_HT_CAP_MCS_TX_UEQM 0x10 /* 802.11n HT IE masks */ #define IEEE80211_HT_IE_CHA_SEC_OFFSET 0x03 +#define IEEE80211_HT_IE_CHA_SEC_NONE 0x00 #define IEEE80211_HT_IE_CHA_SEC_ABOVE 0x01 #define IEEE80211_HT_IE_CHA_SEC_BELOW 0x03 #define IEEE80211_HT_IE_CHA_WIDTH 0x04 -- cgit v1.2.3 From fd7c8a40b2a63863f749e4d17f0d94d2e5ab1331 Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Wed, 11 Jun 2008 14:21:56 -0700 Subject: mac80211: add helpers for frame control testing A few general categories: 1) ieee80211_has_* tests if particular fctl bits are set, the helpers are de in the same order as the fctl defines: A combined _has_a4 was also added to test when both FROMDS and TODS are set. 2) ieee80211_is_* is meant to test whether the frame control is of a certain ftype - data, mgmt, ctl, and two special helpers _is_data_qos, _is_data_pres which also test a subset of the stype space. When testing for a particular stype applicable only to one ftype, functions like ieee80211_is_ack have been added. Note that the ftype is also being checked in these helpers. They have been added for all mgmt and ctl stypes in the same order as the STYPE defines. 3) ieee80211_get_* is meant to take a struct ieee80211_hdr * and returns a pointer to somewhere in the struct, see get_SA, get_DA, get_qos_ctl. The intel wireless drivers had helpers that used this namespace, convert the all to use the new helpers and remove the byteshifting as they were defined in cpu-order rather than little-endian. Signed-off-by: Harvey Harrison Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-3945.c | 36 +-- drivers/net/wireless/iwlwifi/iwl-4965-rs.c | 17 +- drivers/net/wireless/iwlwifi/iwl-4965.c | 28 +- drivers/net/wireless/iwlwifi/iwl-5000.c | 6 +- drivers/net/wireless/iwlwifi/iwl-helpers.h | 109 -------- drivers/net/wireless/iwlwifi/iwl-tx.c | 49 ++-- drivers/net/wireless/iwlwifi/iwl3945-base.c | 33 ++- include/linux/ieee80211.h | 392 ++++++++++++++++++++++++++-- 8 files changed, 457 insertions(+), 213 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c index 0ba6889dfd41..63f20370032d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945.c @@ -388,7 +388,7 @@ static void iwl3945_dbg_report_frame(struct iwl3945_priv *priv, u32 print_dump = 0; /* set to 1 to dump all frames' contents */ u32 hundred = 0; u32 dataframe = 0; - u16 fc; + __le16 fc; u16 seq_ctl; u16 channel; u16 phy_flags; @@ -407,7 +407,7 @@ static void iwl3945_dbg_report_frame(struct iwl3945_priv *priv, u8 *data = IWL_RX_DATA(pkt); /* MAC header */ - fc = le16_to_cpu(header->frame_control); + fc = header->frame_control; seq_ctl = le16_to_cpu(header->seq_ctrl); /* metadata */ @@ -431,8 +431,8 @@ static void iwl3945_dbg_report_frame(struct iwl3945_priv *priv, /* if data frame is to us and all is good, * (optionally) print summary for only 1 out of every 100 */ - if (to_us && (fc & ~IEEE80211_FCTL_PROTECTED) == - (IEEE80211_FCTL_FROMDS | IEEE80211_FTYPE_DATA)) { + if (to_us && (fc & ~cpu_to_le16(IEEE80211_FCTL_PROTECTED)) == + cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FTYPE_DATA)) { dataframe = 1; if (!group100) print_summary = 1; /* print each frame */ @@ -455,13 +455,13 @@ static void iwl3945_dbg_report_frame(struct iwl3945_priv *priv, if (hundred) title = "100Frames"; - else if (fc & IEEE80211_FCTL_RETRY) + else if (ieee80211_has_retry(fc)) title = "Retry"; - else if (ieee80211_is_assoc_response(fc)) + else if (ieee80211_is_assoc_resp(fc)) title = "AscRsp"; - else if (ieee80211_is_reassoc_response(fc)) + else if (ieee80211_is_reassoc_resp(fc)) title = "RasRsp"; - else if (ieee80211_is_probe_response(fc)) { + else if (ieee80211_is_probe_resp(fc)) { title = "PrbRsp"; print_dump = 1; /* dump frame contents */ } else if (ieee80211_is_beacon(fc)) { @@ -490,14 +490,14 @@ static void iwl3945_dbg_report_frame(struct iwl3945_priv *priv, if (dataframe) IWL_DEBUG_RX("%s: mhd=0x%04x, dst=0x%02x, " "len=%u, rssi=%d, chnl=%d, rate=%u, \n", - title, fc, header->addr1[5], + title, le16_to_cpu(fc), header->addr1[5], length, rssi, channel, rate); else { /* src/dst addresses assume managed mode */ IWL_DEBUG_RX("%s: 0x%04x, dst=0x%02x, " "src=0x%02x, rssi=%u, tim=%lu usec, " "phy=0x%02x, chnl=%d\n", - title, fc, header->addr1[5], + title, le16_to_cpu(fc), header->addr1[5], header->addr3[5], rssi, tsf_low - priv->scan_start_tsf, phy_flags, channel); @@ -971,7 +971,7 @@ void iwl3945_hw_build_tx_cmd_rate(struct iwl3945_priv *priv, u8 rts_retry_limit; u8 data_retry_limit; __le32 tx_flags; - u16 fc = le16_to_cpu(hdr->frame_control); + __le16 fc = hdr->frame_control; rate = iwl3945_rates[rate_index].plcp; tx_flags = cmd->cmd.tx.tx_flags; @@ -996,7 +996,7 @@ void iwl3945_hw_build_tx_cmd_rate(struct iwl3945_priv *priv, else rts_retry_limit = 7; - if (ieee80211_is_probe_response(fc)) { + if (ieee80211_is_probe_resp(fc)) { data_retry_limit = 3; if (data_retry_limit < rts_retry_limit) rts_retry_limit = data_retry_limit; @@ -1006,12 +1006,12 @@ void iwl3945_hw_build_tx_cmd_rate(struct iwl3945_priv *priv, if (priv->data_retry_limit != -1) data_retry_limit = priv->data_retry_limit; - if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) { - switch (fc & IEEE80211_FCTL_STYPE) { - case IEEE80211_STYPE_AUTH: - case IEEE80211_STYPE_DEAUTH: - case IEEE80211_STYPE_ASSOC_REQ: - case IEEE80211_STYPE_REASSOC_REQ: + if (ieee80211_is_mgmt(fc)) { + switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) { + case cpu_to_le16(IEEE80211_STYPE_AUTH): + case cpu_to_le16(IEEE80211_STYPE_DEAUTH): + case cpu_to_le16(IEEE80211_STYPE_ASSOC_REQ): + case cpu_to_le16(IEEE80211_STYPE_REASSOC_REQ): if (tx_flags & TX_CMD_FLG_RTS_MSK) { tx_flags &= ~TX_CMD_FLG_RTS_MSK; tx_flags |= TX_CMD_FLG_CTS_MSK; diff --git a/drivers/net/wireless/iwlwifi/iwl-4965-rs.c b/drivers/net/wireless/iwlwifi/iwl-4965-rs.c index d601d31e0de9..202303117285 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965-rs.c @@ -281,11 +281,11 @@ static u8 rs_tl_add_packet(struct iwl4965_lq_sta *lq_data, u32 time_diff; s32 index; struct iwl4965_traffic_load *tl = NULL; - u16 fc = le16_to_cpu(hdr->frame_control); + __le16 fc = hdr->frame_control; u8 tid; - if (ieee80211_is_qos_data(fc)) { - u8 *qc = ieee80211_get_qos_ctrl(hdr, ieee80211_get_hdrlen(fc)); + if (ieee80211_is_data_qos(fc)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); tid = qc[0] & 0xf; } else return MAX_TID_COUNT; @@ -794,7 +794,7 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev, struct iwl4965_scale_tbl_info tbl_type; struct iwl4965_scale_tbl_info *curr_tbl, *search_tbl; u8 active_index = 0; - u16 fc = le16_to_cpu(hdr->frame_control); + __le16 fc = hdr->frame_control; s32 tpt = 0; IWL_DEBUG_RATE_LIMIT("get frame ack response, update rate scale window\n"); @@ -1651,7 +1651,8 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, int high_tpt = IWL_INVALID_VALUE; u32 fail_count; s8 scale_action = 0; - u16 fc, rate_mask; + __le16 fc; + u16 rate_mask; u8 update_lq = 0; struct iwl4965_lq_sta *lq_sta; struct iwl4965_scale_tbl_info *tbl, *tbl1; @@ -1666,7 +1667,7 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, IWL_DEBUG_RATE("rate scale calculate new rate for skb\n"); - fc = le16_to_cpu(hdr->frame_control); + fc = hdr->frame_control; if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1)) { /* Send management frames and broadcast/multicast data using * lowest rate. */ @@ -2100,7 +2101,7 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev, struct ieee80211_conf *conf = &local->hw.conf; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct sta_info *sta; - u16 fc; + __le16 fc; struct iwl_priv *priv = (struct iwl_priv *)priv_rate; struct iwl4965_lq_sta *lq_sta; @@ -2112,7 +2113,7 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev, /* Send management frames and broadcast/multicast data using lowest * rate. */ - fc = le16_to_cpu(hdr->frame_control); + fc = hdr->frame_control; if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1) || !sta || !sta->rate_ctrl_priv) { sel->rate_idx = rate_lowest_index(local, sband, sta); diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index df345cb6fd7d..ab5027345a01 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -2388,7 +2388,7 @@ static void iwl4965_dbg_report_frame(struct iwl_priv *priv, u32 print_dump = 0; /* set to 1 to dump all frames' contents */ u32 hundred = 0; u32 dataframe = 0; - u16 fc; + __le16 fc; u16 seq_ctl; u16 channel; u16 phy_flags; @@ -2411,7 +2411,7 @@ static void iwl4965_dbg_report_frame(struct iwl_priv *priv, return; /* MAC header */ - fc = le16_to_cpu(header->frame_control); + fc = header->frame_control; seq_ctl = le16_to_cpu(header->seq_ctrl); /* metadata */ @@ -2436,8 +2436,8 @@ static void iwl4965_dbg_report_frame(struct iwl_priv *priv, /* if data frame is to us and all is good, * (optionally) print summary for only 1 out of every 100 */ - if (to_us && (fc & ~IEEE80211_FCTL_PROTECTED) == - (IEEE80211_FCTL_FROMDS | IEEE80211_FTYPE_DATA)) { + if (to_us && (fc & ~cpu_to_le16(IEEE80211_FCTL_PROTECTED)) == + cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FTYPE_DATA)) { dataframe = 1; if (!group100) print_summary = 1; /* print each frame */ @@ -2461,13 +2461,13 @@ static void iwl4965_dbg_report_frame(struct iwl_priv *priv, if (hundred) title = "100Frames"; - else if (fc & IEEE80211_FCTL_RETRY) + else if (ieee80211_has_retry(fc)) title = "Retry"; - else if (ieee80211_is_assoc_response(fc)) + else if (ieee80211_is_assoc_resp(fc)) title = "AscRsp"; - else if (ieee80211_is_reassoc_response(fc)) + else if (ieee80211_is_reassoc_resp(fc)) title = "RasRsp"; - else if (ieee80211_is_probe_response(fc)) { + else if (ieee80211_is_probe_resp(fc)) { title = "PrbRsp"; print_dump = 1; /* dump frame contents */ } else if (ieee80211_is_beacon(fc)) { @@ -2496,14 +2496,14 @@ static void iwl4965_dbg_report_frame(struct iwl_priv *priv, if (dataframe) IWL_DEBUG_RX("%s: mhd=0x%04x, dst=0x%02x, " "len=%u, rssi=%d, chnl=%d, rate=%u, \n", - title, fc, header->addr1[5], + title, le16_to_cpu(fc), header->addr1[5], length, rssi, channel, bitrate); else { /* src/dst addresses assume managed mode */ IWL_DEBUG_RX("%s: 0x%04x, dst=0x%02x, " "src=0x%02x, rssi=%u, tim=%lu usec, " "phy=0x%02x, chnl=%d\n", - title, fc, header->addr1[5], + title, le16_to_cpu(fc), header->addr1[5], header->addr3[5], rssi, tsf_low - priv->scan_start_tsf, phy_flags, channel); @@ -3219,7 +3219,7 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv, struct iwl4965_tx_resp *tx_resp = (void *)&pkt->u.raw[0]; u32 status = le32_to_cpu(tx_resp->u.status); int tid = MAX_TID_COUNT, sta_id = IWL_INVALID_STATION; - u16 fc; + __le16 fc; struct ieee80211_hdr *hdr; u8 *qc = NULL; @@ -3235,9 +3235,9 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv, memset(&info->status, 0, sizeof(info->status)); hdr = iwl_tx_queue_get_hdr(priv, txq_id, index); - fc = le16_to_cpu(hdr->frame_control); - if (ieee80211_is_qos_data(fc)) { - qc = ieee80211_get_qos_ctrl(hdr, ieee80211_get_hdrlen(fc)); + fc = hdr->frame_control; + if (ieee80211_is_data_qos(fc)) { + qc = ieee80211_get_qos_ctl(hdr); tid = qc[0] & 0xf; } diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index 8c466b02bdff..438c3812c390 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -1252,7 +1252,6 @@ static void iwl5000_rx_reply_tx(struct iwl_priv *priv, struct iwl5000_tx_resp *tx_resp = (void *)&pkt->u.raw[0]; u32 status = le16_to_cpu(tx_resp->status.status); int tid = MAX_TID_COUNT, sta_id = IWL_INVALID_STATION; - u16 fc; struct ieee80211_hdr *hdr; u8 *qc = NULL; @@ -1268,9 +1267,8 @@ static void iwl5000_rx_reply_tx(struct iwl_priv *priv, memset(&info->status, 0, sizeof(info->status)); hdr = iwl_tx_queue_get_hdr(priv, txq_id, index); - fc = le16_to_cpu(hdr->frame_control); - if (ieee80211_is_qos_data(fc)) { - qc = ieee80211_get_qos_ctrl(hdr, ieee80211_get_hdrlen(fc)); + if (ieee80211_is_data_qos(hdr->frame_control)) { + qc = ieee80211_get_qos_ctl(hdr); tid = qc[0] & 0xf; } diff --git a/drivers/net/wireless/iwlwifi/iwl-helpers.h b/drivers/net/wireless/iwlwifi/iwl-helpers.h index eec423a98fe6..41eed6793328 100644 --- a/drivers/net/wireless/iwlwifi/iwl-helpers.h +++ b/drivers/net/wireless/iwlwifi/iwl-helpers.h @@ -145,115 +145,6 @@ static inline struct ieee80211_conf *ieee80211_get_hw_conf( return &hw->conf; } -#define QOS_CONTROL_LEN 2 - - -static inline int ieee80211_is_management(u16 fc) -{ - return (fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT; -} - -static inline int ieee80211_is_control(u16 fc) -{ - return (fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL; -} - -static inline int ieee80211_is_data(u16 fc) -{ - return (fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA; -} - -static inline int ieee80211_is_back_request(u16 fc) -{ - return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BACK_REQ); -} - -static inline int ieee80211_is_probe_response(u16 fc) -{ - return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP); -} - -static inline int ieee80211_is_probe_request(u16 fc) -{ - return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_REQ); -} - -static inline int ieee80211_is_beacon(u16 fc) -{ - return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON); -} - -static inline int ieee80211_is_atim(u16 fc) -{ - return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ATIM); -} - -static inline int ieee80211_is_assoc_request(u16 fc) -{ - return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_REQ); -} - -static inline int ieee80211_is_assoc_response(u16 fc) -{ - return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_RESP); -} - -static inline int ieee80211_is_auth(u16 fc) -{ - return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_REQ); -} - -static inline int ieee80211_is_deauth(u16 fc) -{ - return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_REQ); -} - -static inline int ieee80211_is_disassoc(u16 fc) -{ - return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_REQ); -} - -static inline int ieee80211_is_reassoc_request(u16 fc) -{ - return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_REASSOC_REQ); -} - -static inline int ieee80211_is_reassoc_response(u16 fc) -{ - return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_REASSOC_RESP); -} - -static inline int ieee80211_is_qos_data(u16 fc) -{ - return ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) && - ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_QOS_DATA); -} -/** - * ieee80211_get_qos_ctrl - get pointer to the QoS control field - * - * This function returns the pointer to 802.11 header QoS field (2 bytes) - * This function doesn't check whether hdr is a QoS hdr, use with care - * @hdr: struct ieee80211_hdr *hdr - * @hdr_len: header length - */ - -static inline u8 *ieee80211_get_qos_ctrl(struct ieee80211_hdr *hdr, int hdr_len) -{ - return ((u8 *) hdr + hdr_len - QOS_CONTROL_LEN); -} - static inline int iwl_check_bits(unsigned long field, unsigned long mask) { return ((field & mask) == mask) ? 1 : 0; diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index e804bf8aea80..98c434c52a64 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -569,15 +569,15 @@ static void iwl_tx_cmd_build_basic(struct iwl_priv *priv, struct ieee80211_hdr *hdr, int is_unicast, u8 std_id) { - u16 fc = le16_to_cpu(hdr->frame_control); + __le16 fc = hdr->frame_control; __le32 tx_flags = tx_cmd->tx_flags; tx_cmd->stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) { tx_flags |= TX_CMD_FLG_ACK_MSK; - if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) + if (ieee80211_is_mgmt(fc)) tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; - if (ieee80211_is_probe_response(fc) && + if (ieee80211_is_probe_resp(fc) && !(le16_to_cpu(hdr->seq_ctrl) & 0xf)) tx_flags |= TX_CMD_FLG_TSF_MSK; } else { @@ -585,7 +585,7 @@ static void iwl_tx_cmd_build_basic(struct iwl_priv *priv, tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; } - if (ieee80211_is_back_request(fc)) + if (ieee80211_is_back_req(fc)) tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK; @@ -593,8 +593,8 @@ static void iwl_tx_cmd_build_basic(struct iwl_priv *priv, if (ieee80211_get_morefrag(hdr)) tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK; - if (ieee80211_is_qos_data(fc)) { - u8 *qc = ieee80211_get_qos_ctrl(hdr, ieee80211_get_hdrlen(fc)); + if (ieee80211_is_data_qos(fc)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); tx_cmd->tid_tspec = qc[0] & 0xf; tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; } else { @@ -613,9 +613,8 @@ static void iwl_tx_cmd_build_basic(struct iwl_priv *priv, tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK; tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK); - if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) { - if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_REQ || - (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_REASSOC_REQ) + if (ieee80211_is_mgmt(fc)) { + if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc)) tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(3); else tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(2); @@ -634,7 +633,7 @@ static void iwl_tx_cmd_build_basic(struct iwl_priv *priv, static void iwl_tx_cmd_build_rate(struct iwl_priv *priv, struct iwl_tx_cmd *tx_cmd, struct ieee80211_tx_info *info, - u16 fc, int sta_id, + __le16 fc, int sta_id, int is_hcca) { u8 rts_retry_limit = 0; @@ -655,7 +654,7 @@ static void iwl_tx_cmd_build_rate(struct iwl_priv *priv, rate_flags |= RATE_MCS_CCK_MSK; - if (ieee80211_is_probe_response(fc)) { + if (ieee80211_is_probe_resp(fc)) { data_retry_limit = 3; if (data_retry_limit < rts_retry_limit) rts_retry_limit = data_retry_limit; @@ -670,11 +669,11 @@ static void iwl_tx_cmd_build_rate(struct iwl_priv *priv, tx_cmd->initial_rate_index = 0; tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK; } else { - switch (fc & IEEE80211_FCTL_STYPE) { - case IEEE80211_STYPE_AUTH: - case IEEE80211_STYPE_DEAUTH: - case IEEE80211_STYPE_ASSOC_REQ: - case IEEE80211_STYPE_REASSOC_REQ: + switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) { + case cpu_to_le16(IEEE80211_STYPE_AUTH): + case cpu_to_le16(IEEE80211_STYPE_DEAUTH): + case cpu_to_le16(IEEE80211_STYPE_ASSOC_REQ): + case cpu_to_le16(IEEE80211_STYPE_REASSOC_REQ): if (tx_cmd->tx_flags & TX_CMD_FLG_RTS_MSK) { tx_cmd->tx_flags &= ~TX_CMD_FLG_RTS_MSK; tx_cmd->tx_flags |= TX_CMD_FLG_CTS_MSK; @@ -771,7 +770,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) u16 seq_number = 0; u8 id, hdr_len, unicast; u8 sta_id; - u16 fc; + __le16 fc; u8 wait_write_ptr = 0; u8 tid = 0; u8 *qc = NULL; @@ -798,19 +797,19 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) unicast = !is_multicast_ether_addr(hdr->addr1); id = 0; - fc = le16_to_cpu(hdr->frame_control); + fc = hdr->frame_control; #ifdef CONFIG_IWLWIFI_DEBUG if (ieee80211_is_auth(fc)) IWL_DEBUG_TX("Sending AUTH frame\n"); - else if (ieee80211_is_assoc_request(fc)) + else if (ieee80211_is_assoc_req(fc)) IWL_DEBUG_TX("Sending ASSOC frame\n"); - else if (ieee80211_is_reassoc_request(fc)) + else if (ieee80211_is_reassoc_req(fc)) IWL_DEBUG_TX("Sending REASSOC frame\n"); #endif /* drop all data frame if we are not associated */ - if (((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) && + if (ieee80211_is_data(fc) && (!iwl_is_associated(priv) || ((priv->iw_mode == IEEE80211_IF_TYPE_STA) && !priv->assoc_id) || !priv->assoc_station_added)) { @@ -820,7 +819,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) spin_unlock_irqrestore(&priv->lock, flags); - hdr_len = ieee80211_get_hdrlen(fc); + hdr_len = ieee80211_get_hdrlen(le16_to_cpu(fc)); /* Find (or create) index into station table for destination station */ sta_id = iwl_get_sta_id(priv, hdr); @@ -834,8 +833,8 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) IWL_DEBUG_TX("station Id %d\n", sta_id); - if (ieee80211_is_qos_data(fc)) { - qc = ieee80211_get_qos_ctrl(hdr, hdr_len); + if (ieee80211_is_data_qos(fc)) { + qc = ieee80211_get_qos_ctl(hdr); tid = qc[0] & 0xf; seq_number = priv->stations[sta_id].tid[tid].seq_number & IEEE80211_SCTL_SEQ; @@ -938,7 +937,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) /* set is_hcca to 0; it probably will never be implemented */ iwl_tx_cmd_build_rate(priv, tx_cmd, info, fc, sta_id, 0); - iwl_update_tx_stats(priv, fc, len); + iwl_update_tx_stats(priv, le16_to_cpu(fc), len); scratch_phys = txcmd_phys + sizeof(struct iwl_cmd_header) + offsetof(struct iwl_tx_cmd, scratch); diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index d0084bcb1eca..0a5bbe9ee938 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -2431,15 +2431,15 @@ static void iwl3945_build_tx_cmd_basic(struct iwl3945_priv *priv, struct ieee80211_hdr *hdr, int is_unicast, u8 std_id) { - u16 fc = le16_to_cpu(hdr->frame_control); + __le16 fc = hdr->frame_control; __le32 tx_flags = cmd->cmd.tx.tx_flags; cmd->cmd.tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) { tx_flags |= TX_CMD_FLG_ACK_MSK; - if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) + if (ieee80211_is_mgmt(fc)) tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; - if (ieee80211_is_probe_response(fc) && + if (ieee80211_is_probe_resp(fc) && !(le16_to_cpu(hdr->seq_ctrl) & 0xf)) tx_flags |= TX_CMD_FLG_TSF_MSK; } else { @@ -2451,8 +2451,8 @@ static void iwl3945_build_tx_cmd_basic(struct iwl3945_priv *priv, if (ieee80211_get_morefrag(hdr)) tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK; - if (ieee80211_is_qos_data(fc)) { - u8 *qc = ieee80211_get_qos_ctrl(hdr, ieee80211_get_hdrlen(fc)); + if (ieee80211_is_data_qos(fc)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); cmd->cmd.tx.tid_tspec = qc[0] & 0xf; tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; } else { @@ -2471,9 +2471,8 @@ static void iwl3945_build_tx_cmd_basic(struct iwl3945_priv *priv, tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK; tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK); - if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) { - if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_REQ || - (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_REASSOC_REQ) + if (ieee80211_is_mgmt(fc)) { + if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc)) cmd->cmd.tx.timeout.pm_frame_timeout = cpu_to_le16(3); else cmd->cmd.tx.timeout.pm_frame_timeout = cpu_to_le16(2); @@ -2564,7 +2563,7 @@ static int iwl3945_tx_skb(struct iwl3945_priv *priv, struct sk_buff *skb) u8 sta_id; u8 tid = 0; u16 seq_number = 0; - u16 fc; + __le16 fc; u8 wait_write_ptr = 0; u8 *qc = NULL; unsigned long flags; @@ -2589,28 +2588,28 @@ static int iwl3945_tx_skb(struct iwl3945_priv *priv, struct sk_buff *skb) unicast = !is_multicast_ether_addr(hdr->addr1); id = 0; - fc = le16_to_cpu(hdr->frame_control); + fc = hdr->frame_control; #ifdef CONFIG_IWL3945_DEBUG if (ieee80211_is_auth(fc)) IWL_DEBUG_TX("Sending AUTH frame\n"); - else if (ieee80211_is_assoc_request(fc)) + else if (ieee80211_is_assoc_req(fc)) IWL_DEBUG_TX("Sending ASSOC frame\n"); - else if (ieee80211_is_reassoc_request(fc)) + else if (ieee80211_is_reassoc_req(fc)) IWL_DEBUG_TX("Sending REASSOC frame\n"); #endif /* drop all data frame if we are not associated */ if ((!iwl3945_is_associated(priv) || ((priv->iw_mode == IEEE80211_IF_TYPE_STA) && !priv->assoc_id)) && - ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)) { + ieee80211_is_data(fc)) { IWL_DEBUG_DROP("Dropping - !iwl3945_is_associated\n"); goto drop_unlock; } spin_unlock_irqrestore(&priv->lock, flags); - hdr_len = ieee80211_get_hdrlen(fc); + hdr_len = ieee80211_get_hdrlen(le16_to_cpu(fc)); /* Find (or create) index into station table for destination station */ sta_id = iwl3945_get_sta_id(priv, hdr); @@ -2624,8 +2623,8 @@ static int iwl3945_tx_skb(struct iwl3945_priv *priv, struct sk_buff *skb) IWL_DEBUG_RATE("station Id %d\n", sta_id); - if (ieee80211_is_qos_data(fc)) { - qc = ieee80211_get_qos_ctrl(hdr, hdr_len); + if (ieee80211_is_data_qos(fc)) { + qc = ieee80211_get_qos_ctl(hdr); tid = qc[0] & 0xf; seq_number = priv->stations[sta_id].tid[tid].seq_number & IEEE80211_SCTL_SEQ; @@ -2746,7 +2745,7 @@ static int iwl3945_tx_skb(struct iwl3945_priv *priv, struct sk_buff *skb) sizeof(out_cmd->cmd.tx)); iwl3945_print_hex_dump(IWL_DL_TX, (u8 *)out_cmd->cmd.tx.hdr, - ieee80211_get_hdrlen(fc)); + ieee80211_get_hdrlen(le16_to_cpu(fc))); /* Tell device the write index *just past* this latest filled TFD */ q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 8f2c20b4a15d..371237b0d8b9 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -98,6 +98,7 @@ #define IEEE80211_MAX_SSID_LEN 32 #define IEEE80211_MAX_MESH_ID_LEN 32 +#define IEEE80211_QOS_CTL_LEN 2 struct ieee80211_hdr { __le16 frame_control; @@ -109,6 +110,355 @@ struct ieee80211_hdr { u8 addr4[6]; } __attribute__ ((packed)); +/** + * ieee80211_has_tods - check if IEEE80211_FCTL_TODS is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_tods(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_TODS)) != 0; +} + +/** + * ieee80211_has_fromds - check if IEEE80211_FCTL_FROMDS is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_fromds(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FROMDS)) != 0; +} + +/** + * ieee80211_has_a4 - check if IEEE80211_FCTL_TODS and IEEE80211_FCTL_FROMDS are set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_a4(__le16 fc) +{ + __le16 tmp = cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS); + return (fc & tmp) == tmp; +} + +/** + * ieee80211_has_morefrags - check if IEEE80211_FCTL_MOREFRAGS is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_morefrags(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_MOREFRAGS)) != 0; +} + +/** + * ieee80211_has_retry - check if IEEE80211_FCTL_RETRY is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_retry(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_RETRY)) != 0; +} + +/** + * ieee80211_has_pm - check if IEEE80211_FCTL_PM is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_pm(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_PM)) != 0; +} + +/** + * ieee80211_has_moredata - check if IEEE80211_FCTL_MOREDATA is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_moredata(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_MOREDATA)) != 0; +} + +/** + * ieee80211_has_protected - check if IEEE80211_FCTL_PROTECTED is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_protected(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_PROTECTED)) != 0; +} + +/** + * ieee80211_has_order - check if IEEE80211_FCTL_ORDER is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_has_order(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_ORDER)) != 0; +} + +/** + * ieee80211_is_mgmt - check if type is IEEE80211_FTYPE_MGMT + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_mgmt(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT); +} + +/** + * ieee80211_is_ctl - check if type is IEEE80211_FTYPE_CTL + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_ctl(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE)) == + cpu_to_le16(IEEE80211_FTYPE_CTL); +} + +/** + * ieee80211_is_data - check if type is IEEE80211_FTYPE_DATA + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_data(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE)) == + cpu_to_le16(IEEE80211_FTYPE_DATA); +} + +/** + * ieee80211_is_data_qos - check if type is IEEE80211_FTYPE_DATA and IEEE80211_STYPE_QOS_DATA is set + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_data_qos(__le16 fc) +{ + /* + * mask with QOS_DATA rather than IEEE80211_FCTL_STYPE as we just need + * to check the one bit + */ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_STYPE_QOS_DATA)) == + cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_DATA); +} + +/** + * ieee80211_is_data_present - check if type is IEEE80211_FTYPE_DATA and has data + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_data_present(__le16 fc) +{ + /* + * mask with 0x40 and test that that bit is clear to only return true + * for the data-containing substypes. + */ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | 0x40)) == + cpu_to_le16(IEEE80211_FTYPE_DATA); +} + +/** + * ieee80211_is_assoc_req - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ASSOC_REQ + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_assoc_req(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ASSOC_REQ); +} + +/** + * ieee80211_is_assoc_resp - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ASSOC_RESP + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_assoc_resp(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ASSOC_RESP); +} + +/** + * ieee80211_is_reassoc_req - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_REASSOC_REQ + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_reassoc_req(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_REASSOC_REQ); +} + +/** + * ieee80211_is_reassoc_resp - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_REASSOC_RESP + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_reassoc_resp(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_REASSOC_RESP); +} + +/** + * ieee80211_is_probe_req - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_PROBE_REQ + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_probe_req(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ); +} + +/** + * ieee80211_is_probe_resp - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_PROBE_RESP + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_probe_resp(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP); +} + +/** + * ieee80211_is_beacon - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_BEACON + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_beacon(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON); +} + +/** + * ieee80211_is_atim - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ATIM + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_atim(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ATIM); +} + +/** + * ieee80211_is_disassoc - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_DISASSOC + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_disassoc(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DISASSOC); +} + +/** + * ieee80211_is_auth - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_AUTH + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_auth(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH); +} + +/** + * ieee80211_is_deauth - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_DEAUTH + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_deauth(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH); +} + +/** + * ieee80211_is_action - check if IEEE80211_FTYPE_MGMT && IEEE80211_STYPE_ACTION + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_action(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); +} + +/** + * ieee80211_is_back_req - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_BACK_REQ + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_back_req(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_BACK_REQ); +} + +/** + * ieee80211_is_back - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_BACK + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_back(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_BACK); +} + +/** + * ieee80211_is_pspoll - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_PSPOLL + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_pspoll(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL); +} + +/** + * ieee80211_is_rts - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_RTS + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_rts(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS); +} + +/** + * ieee80211_is_cts - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CTS + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_cts(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTS); +} + +/** + * ieee80211_is_ack - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_ACK + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_ack(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_ACK); +} + +/** + * ieee80211_is_cfend - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CFEND + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_cfend(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CFEND); +} + +/** + * ieee80211_is_cfendack - check if IEEE80211_FTYPE_CTL && IEEE80211_STYPE_CFENDACK + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_cfendack(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CFENDACK); +} + +/** + * ieee80211_is_nullfunc - check if FTYPE=IEEE80211_FTYPE_DATA and STYPE=IEEE80211_STYPE_NULLFUNC + * @fc: frame control bytes in little-endian byteorder + */ +static inline int ieee80211_is_nullfunc(__le16 fc) +{ + return (fc & cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == + cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC); +} struct ieee80211s_hdr { u8 flags; @@ -552,49 +902,55 @@ enum ieee80211_back_parties { #define WLAN_MAX_KEY_LEN 32 +/** + * ieee80211_get_qos_ctl - get pointer to qos control bytes + * @hdr: the frame + * + * The qos ctrl bytes come after the frame_control, duration, seq_num + * and 3 or 4 addresses of length ETH_ALEN. + * 3 addr: 2 + 2 + 2 + 3*6 = 24 + * 4 addr: 2 + 2 + 2 + 4*6 = 30 + */ +static inline u8 *ieee80211_get_qos_ctl(struct ieee80211_hdr *hdr) +{ + if (ieee80211_has_a4(hdr->frame_control)) + return (u8 *)hdr + 30; + else + return (u8 *)hdr + 24; +} + /** * ieee80211_get_SA - get pointer to SA + * @hdr: the frame * * Given an 802.11 frame, this function returns the offset * to the source address (SA). It does not verify that the * header is long enough to contain the address, and the * header must be long enough to contain the frame control * field. - * - * @hdr: the frame */ static inline u8 *ieee80211_get_SA(struct ieee80211_hdr *hdr) { - __le16 fc = hdr->frame_control; - fc &= cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS); - - switch (fc) { - case __constant_cpu_to_le16(IEEE80211_FCTL_FROMDS): - return hdr->addr3; - case __constant_cpu_to_le16(IEEE80211_FCTL_TODS|IEEE80211_FCTL_FROMDS): + if (ieee80211_has_a4(hdr->frame_control)) return hdr->addr4; - default: - return hdr->addr2; - } + if (ieee80211_has_fromds(hdr->frame_control)) + return hdr->addr3; + return hdr->addr2; } /** * ieee80211_get_DA - get pointer to DA + * @hdr: the frame * * Given an 802.11 frame, this function returns the offset * to the destination address (DA). It does not verify that * the header is long enough to contain the address, and the * header must be long enough to contain the frame control * field. - * - * @hdr: the frame */ static inline u8 *ieee80211_get_DA(struct ieee80211_hdr *hdr) { - __le16 fc = hdr->frame_control; - fc &= cpu_to_le16(IEEE80211_FCTL_TODS); - - if (fc) + if (ieee80211_has_tods(hdr->frame_control)) return hdr->addr3; else return hdr->addr1; -- cgit v1.2.3 From 8b7b1e05b0454f232b8ae1e6ee134b7f0b38abfb Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Wed, 11 Jun 2008 14:21:56 -0700 Subject: mac80211: remove ieee80211_get_morefrag Replaced by the new helper ieee80211_has_morefrags which is more consistent with the intent of the function. Signed-off-by: Harvey Harrison Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-tx.c | 4 ++-- drivers/net/wireless/iwlwifi/iwl3945-base.c | 4 ++-- drivers/net/wireless/rt2x00/rt2x00queue.c | 2 +- drivers/net/wireless/rtl8187_dev.c | 2 +- include/linux/ieee80211.h | 14 -------------- 5 files changed, 6 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index 98c434c52a64..7296e2846ec3 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -590,7 +590,7 @@ static void iwl_tx_cmd_build_basic(struct iwl_priv *priv, tx_cmd->sta_id = std_id; - if (ieee80211_get_morefrag(hdr)) + if (ieee80211_has_morefrags(fc)) tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK; if (ieee80211_is_data_qos(fc)) { @@ -944,7 +944,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys); tx_cmd->dram_msb_ptr = iwl_get_dma_hi_address(scratch_phys); - if (!ieee80211_get_morefrag(hdr)) { + if (!ieee80211_has_morefrags(hdr->frame_control)) { txq->need_update = 1; if (qc) priv->stations[sta_id].tid[tid].seq_number = seq_number; diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 0a5bbe9ee938..47cf4b997f50 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -2448,7 +2448,7 @@ static void iwl3945_build_tx_cmd_basic(struct iwl3945_priv *priv, } cmd->cmd.tx.sta_id = std_id; - if (ieee80211_get_morefrag(hdr)) + if (ieee80211_has_morefrags(fc)) tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK; if (ieee80211_is_data_qos(fc)) { @@ -2731,7 +2731,7 @@ static int iwl3945_tx_skb(struct iwl3945_priv *priv, struct sk_buff *skb) out_cmd->cmd.tx.tx_flags &= ~TX_CMD_FLG_ANT_A_MSK; out_cmd->cmd.tx.tx_flags &= ~TX_CMD_FLG_ANT_B_MSK; - if (!ieee80211_get_morefrag(hdr)) { + if (!ieee80211_has_morefrags(hdr->frame_control)) { txq->need_update = 1; if (qc) { priv->stations[sta_id].tid[tid].seq_number = seq_number; diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index 2f3dd1d91a12..7b52039b01a6 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -130,7 +130,7 @@ void rt2x00queue_create_tx_descriptor(struct queue_entry *entry, /* * Check if more fragments are pending */ - if (ieee80211_get_morefrag(hdr)) { + if (ieee80211_has_morefrags(hdr->frame_control)) { __set_bit(ENTRY_TXD_BURST, &txdesc->flags); __set_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags); } diff --git a/drivers/net/wireless/rtl8187_dev.c b/drivers/net/wireless/rtl8187_dev.c index 0078c7e9918c..bec96d762c6c 100644 --- a/drivers/net/wireless/rtl8187_dev.c +++ b/drivers/net/wireless/rtl8187_dev.c @@ -181,7 +181,7 @@ static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb) flags |= RTL8187_TX_FLAG_NO_ENCRYPT; flags |= ieee80211_get_tx_rate(dev, info)->hw_value << 24; - if (ieee80211_get_morefrag((struct ieee80211_hdr *)skb->data)) + if (ieee80211_has_morefrags(((struct ieee80211_hdr *)skb->data)->frame_control)) flags |= RTL8187_TX_FLAG_MORE_FRAG; if (info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) { flags |= RTL8187_TX_FLAG_RTS; diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 371237b0d8b9..2998e3b5f166 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -956,18 +956,4 @@ static inline u8 *ieee80211_get_DA(struct ieee80211_hdr *hdr) return hdr->addr1; } -/** - * ieee80211_get_morefrag - determine whether the MOREFRAGS bit is set - * - * This function determines whether the "more fragments" bit is set - * in the frame. - * - * @hdr: the frame - */ -static inline int ieee80211_get_morefrag(struct ieee80211_hdr *hdr) -{ - __le16 fc = hdr->frame_control; - return !!(fc & cpu_to_le16(IEEE80211_FCTL_MOREFRAGS)); -} - #endif /* IEEE80211_H */ -- cgit v1.2.3 From d6266281f8175e3ad68c28b20a609b278b47ade5 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Mon, 16 Jun 2008 17:11:50 -0700 Subject: udp: introduce a udp_hashfn function Currently the chain to store a UDP socket is calculated with simple (x & (UDP_HTABLE_SIZE - 1)). But taking net into account would make this calculation a bit more complex, so moving it into a function would help. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- include/linux/udp.h | 5 +++++ net/ipv4/udp.c | 12 ++++++------ net/ipv6/udp.c | 4 ++-- 3 files changed, 13 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/udp.h b/include/linux/udp.h index 581ca2c14c52..9c94312b2de5 100644 --- a/include/linux/udp.h +++ b/include/linux/udp.h @@ -46,6 +46,11 @@ static inline struct udphdr *udp_hdr(const struct sk_buff *skb) #define UDP_HTABLE_SIZE 128 +static inline int udp_hashfn(const unsigned num) +{ + return num & (UDP_HTABLE_SIZE - 1); +} + struct udp_sock { /* inet_sock has to be the first member */ struct inet_sock inet; diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index eba790dcd16b..d8f527d15701 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -134,7 +134,7 @@ static inline int __udp_lib_lport_inuse(struct net *net, __u16 num, struct sock *sk; struct hlist_node *node; - sk_for_each(sk, node, &udptable[num & (UDP_HTABLE_SIZE - 1)]) + sk_for_each(sk, node, &udptable[udp_hashfn(num)]) if (net_eq(sock_net(sk), net) && sk->sk_hash == num) return 1; return 0; @@ -174,7 +174,7 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum, for (i = 0; i < UDP_HTABLE_SIZE; i++) { int size = 0; - head = &udptable[rover & (UDP_HTABLE_SIZE - 1)]; + head = &udptable[udp_hashfn(rover)]; if (hlist_empty(head)) goto gotit; @@ -211,7 +211,7 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum, gotit: snum = rover; } else { - head = &udptable[snum & (UDP_HTABLE_SIZE - 1)]; + head = &udptable[udp_hashfn(snum)]; sk_for_each(sk2, node, head) if (sk2->sk_hash == snum && @@ -227,7 +227,7 @@ gotit: inet_sk(sk)->num = snum; sk->sk_hash = snum; if (sk_unhashed(sk)) { - head = &udptable[snum & (UDP_HTABLE_SIZE - 1)]; + head = &udptable[udp_hashfn(snum)]; sk_add_node(sk, head); sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); } @@ -264,7 +264,7 @@ static struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr, int badness = -1; read_lock(&udp_hash_lock); - sk_for_each(sk, node, &udptable[hnum & (UDP_HTABLE_SIZE - 1)]) { + sk_for_each(sk, node, &udptable[udp_hashfn(hnum)]) { struct inet_sock *inet = inet_sk(sk); if (net_eq(sock_net(sk), net) && sk->sk_hash == hnum && @@ -1068,7 +1068,7 @@ static int __udp4_lib_mcast_deliver(struct sk_buff *skb, int dif; read_lock(&udp_hash_lock); - sk = sk_head(&udptable[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)]); + sk = sk_head(&udptable[udp_hashfn(ntohs(uh->dest))]); dif = skb->dev->ifindex; sk = udp_v4_mcast_next(sk, uh->dest, daddr, uh->source, saddr, dif); if (sk) { diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 09687f7a8564..6e4a822ba651 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -65,7 +65,7 @@ static struct sock *__udp6_lib_lookup(struct net *net, int badness = -1; read_lock(&udp_hash_lock); - sk_for_each(sk, node, &udptable[hnum & (UDP_HTABLE_SIZE - 1)]) { + sk_for_each(sk, node, &udptable[udp_hashfn(hnum)]) { struct inet_sock *inet = inet_sk(sk); if (net_eq(sock_net(sk), net) && sk->sk_hash == hnum && @@ -361,7 +361,7 @@ static int __udp6_lib_mcast_deliver(struct sk_buff *skb, struct in6_addr *saddr, int dif; read_lock(&udp_hash_lock); - sk = sk_head(&udptable[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)]); + sk = sk_head(&udptable[udp_hashfn(ntohs(uh->dest))]); dif = inet6_iif(skb); sk = udp_v6_mcast_next(sk, uh->dest, daddr, uh->source, saddr, dif); if (!sk) { -- cgit v1.2.3 From 19c7578fb22b0aef103222cae9b522f03ae489d6 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Mon, 16 Jun 2008 17:12:29 -0700 Subject: udp: add struct net argument to udp_hashfn Every caller already has this one. The new argument is currently unused, but this will be fixed shortly. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- include/linux/udp.h | 2 +- net/ipv4/udp.c | 12 ++++++------ net/ipv6/udp.c | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/include/linux/udp.h b/include/linux/udp.h index 9c94312b2de5..3deccac2e815 100644 --- a/include/linux/udp.h +++ b/include/linux/udp.h @@ -46,7 +46,7 @@ static inline struct udphdr *udp_hdr(const struct sk_buff *skb) #define UDP_HTABLE_SIZE 128 -static inline int udp_hashfn(const unsigned num) +static inline int udp_hashfn(struct net *net, const unsigned num) { return num & (UDP_HTABLE_SIZE - 1); } diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 6b0acb438f34..11eabf136144 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -134,7 +134,7 @@ static inline int __udp_lib_lport_inuse(struct net *net, __u16 num, struct sock *sk; struct hlist_node *node; - sk_for_each(sk, node, &udptable[udp_hashfn(num)]) + sk_for_each(sk, node, &udptable[udp_hashfn(net, num)]) if (net_eq(sock_net(sk), net) && sk->sk_hash == num) return 1; return 0; @@ -174,7 +174,7 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum, for (i = 0; i < UDP_HTABLE_SIZE; i++) { int size = 0; - head = &udptable[udp_hashfn(rover)]; + head = &udptable[udp_hashfn(net, rover)]; if (hlist_empty(head)) goto gotit; @@ -211,7 +211,7 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum, gotit: snum = rover; } else { - head = &udptable[udp_hashfn(snum)]; + head = &udptable[udp_hashfn(net, snum)]; sk_for_each(sk2, node, head) if (sk2->sk_hash == snum && @@ -227,7 +227,7 @@ gotit: inet_sk(sk)->num = snum; sk->sk_hash = snum; if (sk_unhashed(sk)) { - head = &udptable[udp_hashfn(snum)]; + head = &udptable[udp_hashfn(net, snum)]; sk_add_node(sk, head); sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); } @@ -264,7 +264,7 @@ static struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr, int badness = -1; read_lock(&udp_hash_lock); - sk_for_each(sk, node, &udptable[udp_hashfn(hnum)]) { + sk_for_each(sk, node, &udptable[udp_hashfn(net, hnum)]) { struct inet_sock *inet = inet_sk(sk); if (net_eq(sock_net(sk), net) && sk->sk_hash == hnum && @@ -1068,7 +1068,7 @@ static int __udp4_lib_mcast_deliver(struct net *net, struct sk_buff *skb, int dif; read_lock(&udp_hash_lock); - sk = sk_head(&udptable[udp_hashfn(ntohs(uh->dest))]); + sk = sk_head(&udptable[udp_hashfn(net, ntohs(uh->dest))]); dif = skb->dev->ifindex; sk = udp_v4_mcast_next(sk, uh->dest, daddr, uh->source, saddr, dif); if (sk) { diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 80fb72c48976..432edaa882f6 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -65,7 +65,7 @@ static struct sock *__udp6_lib_lookup(struct net *net, int badness = -1; read_lock(&udp_hash_lock); - sk_for_each(sk, node, &udptable[udp_hashfn(hnum)]) { + sk_for_each(sk, node, &udptable[udp_hashfn(net, hnum)]) { struct inet_sock *inet = inet_sk(sk); if (net_eq(sock_net(sk), net) && sk->sk_hash == hnum && @@ -362,7 +362,7 @@ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb, int dif; read_lock(&udp_hash_lock); - sk = sk_head(&udptable[udp_hashfn(ntohs(uh->dest))]); + sk = sk_head(&udptable[udp_hashfn(net, ntohs(uh->dest))]); dif = inet6_iif(skb); sk = udp_v6_mcast_next(sk, uh->dest, daddr, uh->source, saddr, dif); if (!sk) { -- cgit v1.2.3 From 0b4419162aa6c4204843f3a13b48d9ab821d3167 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Mon, 16 Jun 2008 17:14:11 -0700 Subject: netns: introduce the net_hash_mix "salt" for hashes There are many possible ways to add this "salt", thus I made this patch to be the last in the series to change it if required. Currently I propose to use the struct net pointer itself as this salt, but since this pointer is most often cache-line aligned, shift this right to eliminate the bits, that are most often zeroed. After this, simply add this mix to prepared hashfn-s. For CONFIG_NET_NS=n case this salt is 0 and no changes in hashfn appear. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- include/linux/udp.h | 3 ++- include/net/inet6_hashtables.h | 3 ++- include/net/inet_hashtables.h | 5 +++-- include/net/inet_sock.h | 3 ++- include/net/netns/hash.h | 21 +++++++++++++++++++++ 5 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 include/net/netns/hash.h (limited to 'include/linux') diff --git a/include/linux/udp.h b/include/linux/udp.h index 3deccac2e815..0cf5c4c0ec81 100644 --- a/include/linux/udp.h +++ b/include/linux/udp.h @@ -38,6 +38,7 @@ struct udphdr { #ifdef __KERNEL__ #include #include +#include static inline struct udphdr *udp_hdr(const struct sk_buff *skb) { @@ -48,7 +49,7 @@ static inline struct udphdr *udp_hdr(const struct sk_buff *skb) static inline int udp_hashfn(struct net *net, const unsigned num) { - return num & (UDP_HTABLE_SIZE - 1); + return (num + net_hash_mix(net)) & (UDP_HTABLE_SIZE - 1); } struct udp_sock { diff --git a/include/net/inet6_hashtables.h b/include/net/inet6_hashtables.h index 72f13a9928e4..e48989f04c24 100644 --- a/include/net/inet6_hashtables.h +++ b/include/net/inet6_hashtables.h @@ -24,6 +24,7 @@ #include #include +#include struct inet_hashinfo; @@ -36,7 +37,7 @@ static inline unsigned int inet6_ehashfn(struct net *net, return jhash_3words((__force u32)laddr->s6_addr32[3], (__force u32)faddr->s6_addr32[3], - ports, inet_ehash_secret); + ports, inet_ehash_secret + net_hash_mix(net)); } static inline int inet6_sk_ehashfn(const struct sock *sk) diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h index 26336cdcdc11..bb619d80f2e2 100644 --- a/include/net/inet_hashtables.h +++ b/include/net/inet_hashtables.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -204,7 +205,7 @@ extern void inet_bind_bucket_destroy(struct kmem_cache *cachep, static inline int inet_bhashfn(struct net *net, const __u16 lport, const int bhash_size) { - return lport & (bhash_size - 1); + return (lport + net_hash_mix(net)) & (bhash_size - 1); } extern void inet_bind_hash(struct sock *sk, struct inet_bind_bucket *tb, @@ -213,7 +214,7 @@ extern void inet_bind_hash(struct sock *sk, struct inet_bind_bucket *tb, /* These can have wildcards, don't try too hard. */ static inline int inet_lhashfn(struct net *net, const unsigned short num) { - return num & (INET_LHTABLE_SIZE - 1); + return (num + net_hash_mix(net)) & (INET_LHTABLE_SIZE - 1); } static inline int inet_sk_listen_hashfn(const struct sock *sk) diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index 85bb420c5d86..643e26be058e 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -25,6 +25,7 @@ #include #include #include +#include /** struct ip_options - IP Options * @@ -178,7 +179,7 @@ static inline unsigned int inet_ehashfn(struct net *net, return jhash_3words((__force __u32) laddr, (__force __u32) faddr, ((__u32) lport) << 16 | (__force __u32)fport, - inet_ehash_secret); + inet_ehash_secret + net_hash_mix(net)); } static inline int inet_sk_ehashfn(const struct sock *sk) diff --git a/include/net/netns/hash.h b/include/net/netns/hash.h new file mode 100644 index 000000000000..548d78f2cc47 --- /dev/null +++ b/include/net/netns/hash.h @@ -0,0 +1,21 @@ +#ifndef __NET_NS_HASH_H__ +#define __NET_NS_HASH_H__ + +#include + +struct net; + +static inline unsigned net_hash_mix(struct net *net) +{ +#ifdef CONFIG_NET_NS + /* + * shift this right to eliminate bits, that are + * always zeroed + */ + + return (unsigned)(((unsigned long)net) >> L1_CACHE_SHIFT); +#else + return 0; +#endif +} +#endif -- cgit v1.2.3 From 87de87d5e47f94b4ea647a5bd1bc8dc1f7930db4 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 3 Jun 2008 09:14:03 -0700 Subject: wext: Dispatch and handle compat ioctls entirely in net/wireless/wext.c Next we can kill the hacks in fs/compat_ioctl.c and also dispatch compat ioctls down into the driver and 80211 protocol helper layers in order to handle iw_point objects embedded in stream replies which need to be translated. Signed-off-by: David S. Miller --- fs/compat_ioctl.c | 6 --- include/linux/wireless.h | 13 ++++++ include/net/wext.h | 7 ++++ net/socket.c | 10 +++++ net/wireless/wext.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 134 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index 97dba0d92348..8ab850bf2eee 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -1757,12 +1757,6 @@ static int do_i2c_smbus_ioctl(unsigned int fd, unsigned int cmd, unsigned long a return sys_ioctl(fd, cmd, (unsigned long)tdata); } -struct compat_iw_point { - compat_caddr_t pointer; - __u16 length; - __u16 flags; -}; - static int do_wireless_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) { struct iwreq __user *iwr; diff --git a/include/linux/wireless.h b/include/linux/wireless.h index 4a95a0e5eeca..79d846875825 100644 --- a/include/linux/wireless.h +++ b/include/linux/wireless.h @@ -677,6 +677,19 @@ struct iw_point __u16 flags; /* Optional params */ }; +#ifdef __KERNEL__ +#ifdef CONFIG_COMPAT + +#include + +struct compat_iw_point { + compat_caddr_t pointer; + __u16 length; + __u16 flags; +}; +#endif +#endif + /* * A frequency * For numbers lower than 10^9, we encode the number in 'm' and diff --git a/include/net/wext.h b/include/net/wext.h index 80b31d826b7a..6d76a39a9c5b 100644 --- a/include/net/wext.h +++ b/include/net/wext.h @@ -12,6 +12,8 @@ extern int wext_proc_init(struct net *net); extern void wext_proc_exit(struct net *net); extern int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd, void __user *arg); +extern int compat_wext_handle_ioctl(struct net *net, unsigned int cmd, + unsigned long arg); #else static inline int wext_proc_init(struct net *net) { @@ -26,6 +28,11 @@ static inline int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned { return -EINVAL; } +static inline int compat_wext_handle_ioctl(struct net *net, unsigned int cmd, + unsigned long arg) +{ + return -EINVAL; +} #endif #endif /* __NET_WEXT_H */ diff --git a/net/socket.c b/net/socket.c index 66c4a8cf6db9..81fe82513046 100644 --- a/net/socket.c +++ b/net/socket.c @@ -90,6 +90,7 @@ #include #include +#include #include #include @@ -2210,10 +2211,19 @@ static long compat_sock_ioctl(struct file *file, unsigned cmd, { struct socket *sock = file->private_data; int ret = -ENOIOCTLCMD; + struct sock *sk; + struct net *net; + + sk = sock->sk; + net = sock_net(sk); if (sock->ops->compat_ioctl) ret = sock->ops->compat_ioctl(sock, cmd, arg); + if (ret == -ENOIOCTLCMD && + (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST)) + ret = compat_wext_handle_ioctl(net, cmd, arg); + return ret; } #endif diff --git a/net/wireless/wext.c b/net/wireless/wext.c index 09022cbb58ba..1a4636a9fcde 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext.c @@ -1112,6 +1112,110 @@ int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd, return ret; } +#ifdef CONFIG_COMPAT +static int compat_standard_call(struct net_device *dev, + struct iwreq *iwr, + unsigned int cmd, + iw_handler handler) +{ + const struct iw_ioctl_description *descr; + struct compat_iw_point *iwp_compat; + struct iw_request_info info; + struct iw_point iwp; + int err; + + descr = standard_ioctl + (cmd - SIOCIWFIRST); + + if (descr->header_type != IW_HEADER_TYPE_POINT) + return ioctl_standard_call(dev, iwr, cmd, handler); + + iwp_compat = (struct compat_iw_point *) &iwr->u.data; + iwp.pointer = compat_ptr(iwp_compat->pointer); + iwp.length = iwp_compat->length; + iwp.flags = iwp_compat->flags; + + info.cmd = cmd; + info.flags = 0; + + err = ioctl_standard_iw_point(&iwp, cmd, descr, handler, dev, &info); + + iwp_compat->pointer = ptr_to_compat(iwp.pointer); + iwp_compat->length = iwp.length; + iwp_compat->flags = iwp.flags; + + return err; +} + +static int compat_private_call(struct net_device *dev, struct iwreq *iwr, + unsigned int cmd, iw_handler handler) +{ + const struct iw_priv_args *descr; + struct iw_request_info info; + int ret, extra_size; + + extra_size = get_priv_descr_and_size(dev, cmd, &descr); + + /* Prepare the call */ + info.cmd = cmd; + info.flags = 0; + + /* Check if we have a pointer to user space data or not. */ + if (extra_size == 0) { + /* No extra arguments. Trivial to handle */ + ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u)); + } else { + struct compat_iw_point *iwp_compat; + struct iw_point iwp; + + iwp_compat = (struct compat_iw_point *) &iwr->u.data; + iwp.pointer = compat_ptr(iwp_compat->pointer); + iwp.length = iwp_compat->length; + iwp.flags = iwp_compat->flags; + + ret = ioctl_private_iw_point(&iwp, cmd, descr, + handler, dev, &info, extra_size); + + iwp_compat->pointer = ptr_to_compat(iwp.pointer); + iwp_compat->length = iwp.length; + iwp_compat->flags = iwp.flags; + } + + /* Call commit handler if needed and defined */ + if (ret == -EIWCOMMIT) + ret = call_commit_handler(dev); + + return ret; +} + +int compat_wext_handle_ioctl(struct net *net, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + struct iwreq iwr; + char *colon; + int ret; + + if (copy_from_user(&iwr, argp, sizeof(struct iwreq))) + return -EFAULT; + + iwr.ifr_name[IFNAMSIZ-1] = 0; + colon = strchr(iwr.ifr_name, ':'); + if (colon) + *colon = 0; + + ret = wext_ioctl_dispatch(net, (struct ifreq *) &iwr, cmd, + compat_standard_call, + compat_private_call); + + if (ret >= 0 && + IW_IS_GET(cmd) && + copy_to_user(argp, &iwr, sizeof(struct iwreq))) + return -EFAULT; + + return ret; +} +#endif + /************************* EVENT PROCESSING *************************/ /* * Process events generated by the wireless layer or the driver. -- cgit v1.2.3 From ccc580571cf0799d0460a085a7632b77753f083e Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 16 Jun 2008 18:50:49 -0700 Subject: wext: Emit event stream entries correctly when compat. Three major portions to this change: 1) Add IW_EV_COMPAT_LCP_LEN, IW_EV_COMPAT_POINT_OFF, and IW_EV_COMPAT_POINT_LEN helper defines. 2) Delete iw_stream_check_add_*(), they are unused. 3) Add iw_request_info argument to iwe_stream_add_*(), and use it to size the event and pointer lengths correctly depending upon whether IW_REQUEST_FLAG_COMPAT is set or not. 4) The mechanical transformations to the drivers and wireless stack bits to get the iw_request_info passed down into the routines modified in #3. Also, explicit references to IW_EV_LCP_LEN are replaced with iwe_stream_lcp_len(info). With a lot of help and bug fixes from Masakazu Mokuno. Signed-off-by: David S. Miller --- drivers/net/ps3_gelic_wireless.c | 30 +++--- drivers/net/wireless/airo.c | 43 ++++++--- drivers/net/wireless/atmel.c | 24 +++-- drivers/net/wireless/hostap/hostap.h | 3 +- drivers/net/wireless/hostap/hostap_ap.c | 32 +++---- drivers/net/wireless/hostap/hostap_ioctl.c | 58 +++++------ drivers/net/wireless/libertas/scan.c | 36 +++---- drivers/net/wireless/orinoco.c | 30 +++--- drivers/net/wireless/prism54/isl_ioctl.c | 49 +++++----- drivers/net/wireless/rndis_wlan.c | 32 ++++--- drivers/net/wireless/wl3501_cs.c | 10 +- drivers/net/wireless/zd1201.c | 21 ++-- include/linux/wireless.h | 15 +++ include/net/iw_handler.h | 149 ++++++++++------------------- net/ieee80211/ieee80211_wx.c | 48 ++++++---- net/mac80211/ieee80211_i.h | 5 +- net/mac80211/mlme.c | 66 +++++++------ net/mac80211/wext.c | 2 +- 18 files changed, 345 insertions(+), 308 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/ps3_gelic_wireless.c b/drivers/net/ps3_gelic_wireless.c index aa963ac1e37b..6b2dee0cf3a9 100644 --- a/drivers/net/ps3_gelic_wireless.c +++ b/drivers/net/ps3_gelic_wireless.c @@ -571,6 +571,7 @@ static void gelic_wl_parse_ie(u8 *data, size_t len, * independent format */ static char *gelic_wl_translate_scan(struct net_device *netdev, + struct iw_request_info *info, char *ev, char *stop, struct gelic_wl_scan_info *network) @@ -588,26 +589,26 @@ static char *gelic_wl_translate_scan(struct net_device *netdev, iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, &scan->bssid[2], ETH_ALEN); - ev = iwe_stream_add_event(ev, stop, &iwe, IW_EV_ADDR_LEN); + ev = iwe_stream_add_event(info, ev, stop, &iwe, IW_EV_ADDR_LEN); /* ESSID */ iwe.cmd = SIOCGIWESSID; iwe.u.data.flags = 1; iwe.u.data.length = strnlen(scan->essid, 32); - ev = iwe_stream_add_point(ev, stop, &iwe, scan->essid); + ev = iwe_stream_add_point(info, ev, stop, &iwe, scan->essid); /* FREQUENCY */ iwe.cmd = SIOCGIWFREQ; iwe.u.freq.m = be16_to_cpu(scan->channel); iwe.u.freq.e = 0; /* table value in MHz */ iwe.u.freq.i = 0; - ev = iwe_stream_add_event(ev, stop, &iwe, IW_EV_FREQ_LEN); + ev = iwe_stream_add_event(info, ev, stop, &iwe, IW_EV_FREQ_LEN); /* RATES */ iwe.cmd = SIOCGIWRATE; iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; /* to stuff multiple values in one event */ - tmp = ev + IW_EV_LCP_LEN; + tmp = ev + iwe_stream_lcp_len(info); /* put them in ascendant order (older is first) */ i = 0; j = 0; @@ -620,16 +621,16 @@ static char *gelic_wl_translate_scan(struct net_device *netdev, else rate = scan->rate[i++] & 0x7f; iwe.u.bitrate.value = rate * 500000; /* 500kbps unit */ - tmp = iwe_stream_add_value(ev, tmp, stop, &iwe, + tmp = iwe_stream_add_value(info, ev, tmp, stop, &iwe, IW_EV_PARAM_LEN); } while (j < network->rate_ext_len) { iwe.u.bitrate.value = (scan->ext_rate[j++] & 0x7f) * 500000; - tmp = iwe_stream_add_value(ev, tmp, stop, &iwe, + tmp = iwe_stream_add_value(info, ev, tmp, stop, &iwe, IW_EV_PARAM_LEN); } /* Check if we added any rate */ - if (IW_EV_LCP_LEN < (tmp - ev)) + if (iwe_stream_lcp_len(info) < (tmp - ev)) ev = tmp; /* ENCODE */ @@ -639,7 +640,7 @@ static char *gelic_wl_translate_scan(struct net_device *netdev, else iwe.u.data.flags = IW_ENCODE_DISABLED; iwe.u.data.length = 0; - ev = iwe_stream_add_point(ev, stop, &iwe, scan->essid); + ev = iwe_stream_add_point(info, ev, stop, &iwe, scan->essid); /* MODE */ iwe.cmd = SIOCGIWMODE; @@ -649,7 +650,7 @@ static char *gelic_wl_translate_scan(struct net_device *netdev, iwe.u.mode = IW_MODE_MASTER; else iwe.u.mode = IW_MODE_ADHOC; - ev = iwe_stream_add_event(ev, stop, &iwe, IW_EV_UINT_LEN); + ev = iwe_stream_add_event(info, ev, stop, &iwe, IW_EV_UINT_LEN); } /* QUAL */ @@ -659,7 +660,7 @@ static char *gelic_wl_translate_scan(struct net_device *netdev, iwe.u.qual.level = be16_to_cpu(scan->rssi); iwe.u.qual.qual = be16_to_cpu(scan->rssi); iwe.u.qual.noise = 0; - ev = iwe_stream_add_event(ev, stop, &iwe, IW_EV_QUAL_LEN); + ev = iwe_stream_add_event(info, ev, stop, &iwe, IW_EV_QUAL_LEN); /* RSN */ memset(&iwe, 0, sizeof(iwe)); @@ -669,7 +670,7 @@ static char *gelic_wl_translate_scan(struct net_device *netdev, if (len) { iwe.cmd = IWEVGENIE; iwe.u.data.length = len; - ev = iwe_stream_add_point(ev, stop, &iwe, buf); + ev = iwe_stream_add_point(info, ev, stop, &iwe, buf); } } else { /* this scan info has IE data */ @@ -684,7 +685,7 @@ static char *gelic_wl_translate_scan(struct net_device *netdev, memcpy(buf, ie_info.wpa.data, ie_info.wpa.len); iwe.cmd = IWEVGENIE; iwe.u.data.length = ie_info.wpa.len; - ev = iwe_stream_add_point(ev, stop, &iwe, buf); + ev = iwe_stream_add_point(info, ev, stop, &iwe, buf); } if (ie_info.rsn.len && (ie_info.rsn.len <= sizeof(buf))) { @@ -692,7 +693,7 @@ static char *gelic_wl_translate_scan(struct net_device *netdev, memcpy(buf, ie_info.rsn.data, ie_info.rsn.len); iwe.cmd = IWEVGENIE; iwe.u.data.length = ie_info.rsn.len; - ev = iwe_stream_add_point(ev, stop, &iwe, buf); + ev = iwe_stream_add_point(info, ev, stop, &iwe, buf); } } @@ -737,7 +738,8 @@ static int gelic_wl_get_scan(struct net_device *netdev, if (wl->scan_age == 0 || time_after(scan_info->last_scanned + wl->scan_age, this_time)) - ev = gelic_wl_translate_scan(netdev, ev, stop, + ev = gelic_wl_translate_scan(netdev, info, + ev, stop, scan_info); else pr_debug("%s:entry too old\n", __func__); diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index e30f8b79ea89..73d66a80c4a3 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -7156,6 +7156,7 @@ out: * format that the Wireless Tools will understand - Jean II */ static inline char *airo_translate_scan(struct net_device *dev, + struct iw_request_info *info, char *current_ev, char *end_buf, BSSListRid *bss) @@ -7172,7 +7173,8 @@ static inline char *airo_translate_scan(struct net_device *dev, iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN); - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_ADDR_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_ADDR_LEN); /* Other entries will be displayed in the order we give them */ @@ -7182,7 +7184,8 @@ static inline char *airo_translate_scan(struct net_device *dev, iwe.u.data.length = 32; iwe.cmd = SIOCGIWESSID; iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, bss->ssid); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss->ssid); /* Add mode */ iwe.cmd = SIOCGIWMODE; @@ -7192,7 +7195,8 @@ static inline char *airo_translate_scan(struct net_device *dev, iwe.u.mode = IW_MODE_MASTER; else iwe.u.mode = IW_MODE_ADHOC; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_UINT_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_UINT_LEN); } /* Add frequency */ @@ -7203,7 +7207,8 @@ static inline char *airo_translate_scan(struct net_device *dev, */ iwe.u.freq.m = frequency_list[iwe.u.freq.m - 1] * 100000; iwe.u.freq.e = 1; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_FREQ_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_FREQ_LEN); dBm = le16_to_cpu(bss->dBm); @@ -7223,7 +7228,8 @@ static inline char *airo_translate_scan(struct net_device *dev, | IW_QUAL_DBM; } iwe.u.qual.noise = ai->wstats.qual.noise; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_QUAL_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_QUAL_LEN); /* Add encryption capability */ iwe.cmd = SIOCGIWENCODE; @@ -7232,11 +7238,12 @@ static inline char *airo_translate_scan(struct net_device *dev, else iwe.u.data.flags = IW_ENCODE_DISABLED; iwe.u.data.length = 0; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, bss->ssid); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss->ssid); /* Rate : stuffing multiple values in a single event require a bit * more of magic - Jean II */ - current_val = current_ev + IW_EV_LCP_LEN; + current_val = current_ev + iwe_stream_lcp_len(info); iwe.cmd = SIOCGIWRATE; /* Those two flags are ignored... */ @@ -7249,10 +7256,12 @@ static inline char *airo_translate_scan(struct net_device *dev, /* Bit rate given in 500 kb/s units (+ 0x80) */ iwe.u.bitrate.value = ((bss->rates[i] & 0x7f) * 500000); /* Add new value to event */ - current_val = iwe_stream_add_value(current_ev, current_val, end_buf, &iwe, IW_EV_PARAM_LEN); + current_val = iwe_stream_add_value(info, current_ev, + current_val, end_buf, + &iwe, IW_EV_PARAM_LEN); } /* Check if we added any event */ - if((current_val - current_ev) > IW_EV_LCP_LEN) + if ((current_val - current_ev) > iwe_stream_lcp_len(info)) current_ev = current_val; /* Beacon interval */ @@ -7261,7 +7270,8 @@ static inline char *airo_translate_scan(struct net_device *dev, iwe.cmd = IWEVCUSTOM; sprintf(buf, "bcn_int=%d", bss->beaconInterval); iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, buf); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, buf); kfree(buf); } @@ -7295,8 +7305,10 @@ static inline char *airo_translate_scan(struct net_device *dev, iwe.cmd = IWEVGENIE; iwe.u.data.length = min(info_element->len + 2, MAX_WPA_IE_LEN); - current_ev = iwe_stream_add_point(current_ev, end_buf, - &iwe, (char *) info_element); + current_ev = iwe_stream_add_point( + info, current_ev, + end_buf, &iwe, + (char *) info_element); } break; @@ -7304,8 +7316,9 @@ static inline char *airo_translate_scan(struct net_device *dev, iwe.cmd = IWEVGENIE; iwe.u.data.length = min(info_element->len + 2, MAX_WPA_IE_LEN); - current_ev = iwe_stream_add_point(current_ev, end_buf, - &iwe, (char *) info_element); + current_ev = iwe_stream_add_point( + info, current_ev, end_buf, + &iwe, (char *) info_element); break; default: @@ -7344,7 +7357,7 @@ static int airo_get_scan(struct net_device *dev, list_for_each_entry (net, &ai->network_list, list) { /* Translate to WE format this entry */ - current_ev = airo_translate_scan(dev, current_ev, + current_ev = airo_translate_scan(dev, info, current_ev, extra + dwrq->length, &net->bss); diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c index 7bb2646ae0ef..28b6ff3eaa37 100644 --- a/drivers/net/wireless/atmel.c +++ b/drivers/net/wireless/atmel.c @@ -2310,30 +2310,40 @@ static int atmel_get_scan(struct net_device *dev, iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, priv->BSSinfo[i].BSSID, 6); - current_ev = iwe_stream_add_event(current_ev, extra + IW_SCAN_MAX_DATA, &iwe, IW_EV_ADDR_LEN); + current_ev = iwe_stream_add_event(info, current_ev, + extra + IW_SCAN_MAX_DATA, + &iwe, IW_EV_ADDR_LEN); iwe.u.data.length = priv->BSSinfo[i].SSIDsize; if (iwe.u.data.length > 32) iwe.u.data.length = 32; iwe.cmd = SIOCGIWESSID; iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(current_ev, extra + IW_SCAN_MAX_DATA, &iwe, priv->BSSinfo[i].SSID); + current_ev = iwe_stream_add_point(info, current_ev, + extra + IW_SCAN_MAX_DATA, + &iwe, priv->BSSinfo[i].SSID); iwe.cmd = SIOCGIWMODE; iwe.u.mode = priv->BSSinfo[i].BSStype; - current_ev = iwe_stream_add_event(current_ev, extra + IW_SCAN_MAX_DATA, &iwe, IW_EV_UINT_LEN); + current_ev = iwe_stream_add_event(info, current_ev, + extra + IW_SCAN_MAX_DATA, + &iwe, IW_EV_UINT_LEN); iwe.cmd = SIOCGIWFREQ; iwe.u.freq.m = priv->BSSinfo[i].channel; iwe.u.freq.e = 0; - current_ev = iwe_stream_add_event(current_ev, extra + IW_SCAN_MAX_DATA, &iwe, IW_EV_FREQ_LEN); + current_ev = iwe_stream_add_event(info, current_ev, + extra + IW_SCAN_MAX_DATA, + &iwe, IW_EV_FREQ_LEN); /* Add quality statistics */ iwe.cmd = IWEVQUAL; iwe.u.qual.level = priv->BSSinfo[i].RSSI; iwe.u.qual.qual = iwe.u.qual.level; /* iwe.u.qual.noise = SOMETHING */ - current_ev = iwe_stream_add_event(current_ev, extra + IW_SCAN_MAX_DATA , &iwe, IW_EV_QUAL_LEN); + current_ev = iwe_stream_add_event(info, current_ev, + extra + IW_SCAN_MAX_DATA, + &iwe, IW_EV_QUAL_LEN); iwe.cmd = SIOCGIWENCODE; @@ -2342,7 +2352,9 @@ static int atmel_get_scan(struct net_device *dev, else iwe.u.data.flags = IW_ENCODE_DISABLED; iwe.u.data.length = 0; - current_ev = iwe_stream_add_point(current_ev, extra + IW_SCAN_MAX_DATA, &iwe, NULL); + current_ev = iwe_stream_add_point(info, current_ev, + extra + IW_SCAN_MAX_DATA, + &iwe, NULL); } /* Length of data */ diff --git a/drivers/net/wireless/hostap/hostap.h b/drivers/net/wireless/hostap/hostap.h index 547ba84dc797..3a386a636cca 100644 --- a/drivers/net/wireless/hostap/hostap.h +++ b/drivers/net/wireless/hostap/hostap.h @@ -67,7 +67,8 @@ void * ap_crypt_get_ptrs(struct ap_data *ap, u8 *addr, int permanent, int prism2_ap_get_sta_qual(local_info_t *local, struct sockaddr addr[], struct iw_quality qual[], int buf_size, int aplist); -int prism2_ap_translate_scan(struct net_device *dev, char *buffer); +int prism2_ap_translate_scan(struct net_device *dev, + struct iw_request_info *info, char *buffer); int prism2_hostapd(struct ap_data *ap, struct prism2_hostapd_param *param); diff --git a/drivers/net/wireless/hostap/hostap_ap.c b/drivers/net/wireless/hostap/hostap_ap.c index 0acd9589c48c..06b23df8f69b 100644 --- a/drivers/net/wireless/hostap/hostap_ap.c +++ b/drivers/net/wireless/hostap/hostap_ap.c @@ -2420,7 +2420,8 @@ int prism2_ap_get_sta_qual(local_info_t *local, struct sockaddr addr[], /* Translate our list of Access Points & Stations to a card independant * format that the Wireless Tools will understand - Jean II */ -int prism2_ap_translate_scan(struct net_device *dev, char *buffer) +int prism2_ap_translate_scan(struct net_device *dev, + struct iw_request_info *info, char *buffer) { struct hostap_interface *iface; local_info_t *local; @@ -2449,8 +2450,8 @@ int prism2_ap_translate_scan(struct net_device *dev, char *buffer) iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, sta->addr, ETH_ALEN); iwe.len = IW_EV_ADDR_LEN; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, - IW_EV_ADDR_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_ADDR_LEN); /* Use the mode to indicate if it's a station or * an Access Point */ @@ -2461,8 +2462,8 @@ int prism2_ap_translate_scan(struct net_device *dev, char *buffer) else iwe.u.mode = IW_MODE_INFRA; iwe.len = IW_EV_UINT_LEN; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, - IW_EV_UINT_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_UINT_LEN); /* Some quality */ memset(&iwe, 0, sizeof(iwe)); @@ -2477,8 +2478,8 @@ int prism2_ap_translate_scan(struct net_device *dev, char *buffer) iwe.u.qual.noise = HFA384X_LEVEL_TO_dBm(sta->last_rx_silence); iwe.u.qual.updated = sta->last_rx_updated; iwe.len = IW_EV_QUAL_LEN; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, - IW_EV_QUAL_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_QUAL_LEN); #ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT if (sta->ap) { @@ -2486,8 +2487,8 @@ int prism2_ap_translate_scan(struct net_device *dev, char *buffer) iwe.cmd = SIOCGIWESSID; iwe.u.data.length = sta->u.ap.ssid_len; iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(current_ev, end_buf, - &iwe, + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, sta->u.ap.ssid); memset(&iwe, 0, sizeof(iwe)); @@ -2497,10 +2498,9 @@ int prism2_ap_translate_scan(struct net_device *dev, char *buffer) IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; else iwe.u.data.flags = IW_ENCODE_DISABLED; - current_ev = iwe_stream_add_point(current_ev, end_buf, - &iwe, - sta->u.ap.ssid - /* 0 byte memcpy */); + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, + sta->u.ap.ssid); if (sta->u.ap.channel > 0 && sta->u.ap.channel <= FREQ_COUNT) { @@ -2510,7 +2510,7 @@ int prism2_ap_translate_scan(struct net_device *dev, char *buffer) * 100000; iwe.u.freq.e = 1; current_ev = iwe_stream_add_event( - current_ev, end_buf, &iwe, + info, current_ev, end_buf, &iwe, IW_EV_FREQ_LEN); } @@ -2519,8 +2519,8 @@ int prism2_ap_translate_scan(struct net_device *dev, char *buffer) sprintf(buf, "beacon_interval=%d", sta->listen_interval); iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, - &iwe, buf); + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, buf); } #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ diff --git a/drivers/net/wireless/hostap/hostap_ioctl.c b/drivers/net/wireless/hostap/hostap_ioctl.c index 0ca0bfeb0ada..ed52d98317cd 100644 --- a/drivers/net/wireless/hostap/hostap_ioctl.c +++ b/drivers/net/wireless/hostap/hostap_ioctl.c @@ -1793,6 +1793,7 @@ static int prism2_ioctl_siwscan(struct net_device *dev, #ifndef PRISM2_NO_STATION_MODES static char * __prism2_translate_scan(local_info_t *local, + struct iw_request_info *info, struct hfa384x_hostscan_result *scan, struct hostap_bss_info *bss, char *current_ev, char *end_buf) @@ -1823,7 +1824,7 @@ static char * __prism2_translate_scan(local_info_t *local, iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, bssid, ETH_ALEN); - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_ADDR_LEN); /* Other entries will be displayed in the order we give them */ @@ -1832,7 +1833,8 @@ static char * __prism2_translate_scan(local_info_t *local, iwe.cmd = SIOCGIWESSID; iwe.u.data.length = ssid_len; iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, ssid); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, ssid); memset(&iwe, 0, sizeof(iwe)); iwe.cmd = SIOCGIWMODE; @@ -1847,8 +1849,8 @@ static char * __prism2_translate_scan(local_info_t *local, iwe.u.mode = IW_MODE_MASTER; else iwe.u.mode = IW_MODE_ADHOC; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, - IW_EV_UINT_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_UINT_LEN); } memset(&iwe, 0, sizeof(iwe)); @@ -1864,8 +1866,8 @@ static char * __prism2_translate_scan(local_info_t *local, if (chan > 0) { iwe.u.freq.m = freq_list[chan - 1] * 100000; iwe.u.freq.e = 1; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, - IW_EV_FREQ_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_FREQ_LEN); } if (scan) { @@ -1884,8 +1886,8 @@ static char * __prism2_translate_scan(local_info_t *local, | IW_QUAL_NOISE_UPDATED | IW_QUAL_QUAL_INVALID | IW_QUAL_DBM; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, - IW_EV_QUAL_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_QUAL_LEN); } memset(&iwe, 0, sizeof(iwe)); @@ -1895,13 +1897,13 @@ static char * __prism2_translate_scan(local_info_t *local, else iwe.u.data.flags = IW_ENCODE_DISABLED; iwe.u.data.length = 0; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, ""); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, &iwe, ""); /* TODO: add SuppRates into BSS table */ if (scan) { memset(&iwe, 0, sizeof(iwe)); iwe.cmd = SIOCGIWRATE; - current_val = current_ev + IW_EV_LCP_LEN; + current_val = current_ev + iwe_stream_lcp_len(info); pos = scan->sup_rates; for (i = 0; i < sizeof(scan->sup_rates); i++) { if (pos[i] == 0) @@ -1909,11 +1911,11 @@ static char * __prism2_translate_scan(local_info_t *local, /* Bit rate given in 500 kb/s units (+ 0x80) */ iwe.u.bitrate.value = ((pos[i] & 0x7f) * 500000); current_val = iwe_stream_add_value( - current_ev, current_val, end_buf, &iwe, + info, current_ev, current_val, end_buf, &iwe, IW_EV_PARAM_LEN); } /* Check if we added any event */ - if ((current_val - current_ev) > IW_EV_LCP_LEN) + if ((current_val - current_ev) > iwe_stream_lcp_len(info)) current_ev = current_val; } @@ -1924,15 +1926,15 @@ static char * __prism2_translate_scan(local_info_t *local, iwe.cmd = IWEVCUSTOM; sprintf(buf, "bcn_int=%d", le16_to_cpu(scan->beacon_interval)); iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, - buf); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, buf); memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVCUSTOM; sprintf(buf, "resp_rate=%d", le16_to_cpu(scan->rate)); iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, - buf); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, buf); if (local->last_scan_type == PRISM2_HOSTSCAN && (capabilities & WLAN_CAPABILITY_IBSS)) { @@ -1940,8 +1942,8 @@ static char * __prism2_translate_scan(local_info_t *local, iwe.cmd = IWEVCUSTOM; sprintf(buf, "atim=%d", le16_to_cpu(scan->atim)); iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, - &iwe, buf); + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, buf); } } kfree(buf); @@ -1950,16 +1952,16 @@ static char * __prism2_translate_scan(local_info_t *local, memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVGENIE; iwe.u.data.length = bss->wpa_ie_len; - current_ev = iwe_stream_add_point( - current_ev, end_buf, &iwe, bss->wpa_ie); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss->wpa_ie); } if (bss && bss->rsn_ie_len > 0 && bss->rsn_ie_len <= MAX_WPA_IE_LEN) { memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVGENIE; iwe.u.data.length = bss->rsn_ie_len; - current_ev = iwe_stream_add_point( - current_ev, end_buf, &iwe, bss->rsn_ie); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss->rsn_ie); } return current_ev; @@ -1969,6 +1971,7 @@ static char * __prism2_translate_scan(local_info_t *local, /* Translate scan data returned from the card to a card independant * format that the Wireless Tools will understand - Jean II */ static inline int prism2_translate_scan(local_info_t *local, + struct iw_request_info *info, char *buffer, int buflen) { struct hfa384x_hostscan_result *scan; @@ -1999,13 +2002,14 @@ static inline int prism2_translate_scan(local_info_t *local, if (memcmp(bss->bssid, scan->bssid, ETH_ALEN) == 0) { bss->included = 1; current_ev = __prism2_translate_scan( - local, scan, bss, current_ev, end_buf); + local, info, scan, bss, current_ev, + end_buf); found++; } } if (!found) { current_ev = __prism2_translate_scan( - local, scan, NULL, current_ev, end_buf); + local, info, scan, NULL, current_ev, end_buf); } /* Check if there is space for one more entry */ if ((end_buf - current_ev) <= IW_EV_ADDR_LEN) { @@ -2023,7 +2027,7 @@ static inline int prism2_translate_scan(local_info_t *local, bss = list_entry(ptr, struct hostap_bss_info, list); if (bss->included) continue; - current_ev = __prism2_translate_scan(local, NULL, bss, + current_ev = __prism2_translate_scan(local, info, NULL, bss, current_ev, end_buf); /* Check if there is space for one more entry */ if ((end_buf - current_ev) <= IW_EV_ADDR_LEN) { @@ -2070,7 +2074,7 @@ static inline int prism2_ioctl_giwscan_sta(struct net_device *dev, } local->scan_timestamp = 0; - res = prism2_translate_scan(local, extra, data->length); + res = prism2_translate_scan(local, info, extra, data->length); if (res >= 0) { data->length = res; @@ -2103,7 +2107,7 @@ static int prism2_ioctl_giwscan(struct net_device *dev, * Jean II */ /* Translate to WE format */ - res = prism2_ap_translate_scan(dev, extra); + res = prism2_ap_translate_scan(dev, info, extra); if (res >= 0) { printk(KERN_DEBUG "Scan result translation succeeded " "(length=%d)\n", res); diff --git a/drivers/net/wireless/libertas/scan.c b/drivers/net/wireless/libertas/scan.c index d448c9702a0f..343ed38f772d 100644 --- a/drivers/net/wireless/libertas/scan.c +++ b/drivers/net/wireless/libertas/scan.c @@ -776,8 +776,9 @@ out: #define MAX_CUSTOM_LEN 64 static inline char *lbs_translate_scan(struct lbs_private *priv, - char *start, char *stop, - struct bss_descriptor *bss) + struct iw_request_info *info, + char *start, char *stop, + struct bss_descriptor *bss) { struct chan_freq_power *cfp; char *current_val; /* For rates */ @@ -801,24 +802,24 @@ static inline char *lbs_translate_scan(struct lbs_private *priv, iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, &bss->bssid, ETH_ALEN); - start = iwe_stream_add_event(start, stop, &iwe, IW_EV_ADDR_LEN); + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN); /* SSID */ iwe.cmd = SIOCGIWESSID; iwe.u.data.flags = 1; iwe.u.data.length = min((uint32_t) bss->ssid_len, (uint32_t) IW_ESSID_MAX_SIZE); - start = iwe_stream_add_point(start, stop, &iwe, bss->ssid); + start = iwe_stream_add_point(info, start, stop, &iwe, bss->ssid); /* Mode */ iwe.cmd = SIOCGIWMODE; iwe.u.mode = bss->mode; - start = iwe_stream_add_event(start, stop, &iwe, IW_EV_UINT_LEN); + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_UINT_LEN); /* Frequency */ iwe.cmd = SIOCGIWFREQ; iwe.u.freq.m = (long)cfp->freq * 100000; iwe.u.freq.e = 1; - start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN); + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN); /* Add quality statistics */ iwe.cmd = IWEVQUAL; @@ -852,7 +853,7 @@ static inline char *lbs_translate_scan(struct lbs_private *priv, nf = priv->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE; iwe.u.qual.level = CAL_RSSI(snr, nf); } - start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN); + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN); /* Add encryption capability */ iwe.cmd = SIOCGIWENCODE; @@ -862,9 +863,9 @@ static inline char *lbs_translate_scan(struct lbs_private *priv, iwe.u.data.flags = IW_ENCODE_DISABLED; } iwe.u.data.length = 0; - start = iwe_stream_add_point(start, stop, &iwe, bss->ssid); + start = iwe_stream_add_point(info, start, stop, &iwe, bss->ssid); - current_val = start + IW_EV_LCP_LEN; + current_val = start + iwe_stream_lcp_len(info); iwe.cmd = SIOCGIWRATE; iwe.u.bitrate.fixed = 0; @@ -874,19 +875,19 @@ static inline char *lbs_translate_scan(struct lbs_private *priv, for (j = 0; bss->rates[j] && (j < sizeof(bss->rates)); j++) { /* Bit rate given in 500 kb/s units */ iwe.u.bitrate.value = bss->rates[j] * 500000; - current_val = iwe_stream_add_value(start, current_val, - stop, &iwe, IW_EV_PARAM_LEN); + current_val = iwe_stream_add_value(info, start, current_val, + stop, &iwe, IW_EV_PARAM_LEN); } if ((bss->mode == IW_MODE_ADHOC) && priv->adhoccreate && !lbs_ssid_cmp(priv->curbssparams.ssid, priv->curbssparams.ssid_len, bss->ssid, bss->ssid_len)) { iwe.u.bitrate.value = 22 * 500000; - current_val = iwe_stream_add_value(start, current_val, + current_val = iwe_stream_add_value(info, start, current_val, stop, &iwe, IW_EV_PARAM_LEN); } /* Check if we added any event */ - if((current_val - start) > IW_EV_LCP_LEN) + if ((current_val - start) > iwe_stream_lcp_len(info)) start = current_val; memset(&iwe, 0, sizeof(iwe)); @@ -895,7 +896,7 @@ static inline char *lbs_translate_scan(struct lbs_private *priv, memcpy(buf, bss->wpa_ie, bss->wpa_ie_len); iwe.cmd = IWEVGENIE; iwe.u.data.length = bss->wpa_ie_len; - start = iwe_stream_add_point(start, stop, &iwe, buf); + start = iwe_stream_add_point(info, start, stop, &iwe, buf); } memset(&iwe, 0, sizeof(iwe)); @@ -904,7 +905,7 @@ static inline char *lbs_translate_scan(struct lbs_private *priv, memcpy(buf, bss->rsn_ie, bss->rsn_ie_len); iwe.cmd = IWEVGENIE; iwe.u.data.length = bss->rsn_ie_len; - start = iwe_stream_add_point(start, stop, &iwe, buf); + start = iwe_stream_add_point(info, start, stop, &iwe, buf); } if (bss->mesh) { @@ -915,7 +916,8 @@ static inline char *lbs_translate_scan(struct lbs_private *priv, p += snprintf(p, MAX_CUSTOM_LEN, "mesh-type: olpc"); iwe.u.data.length = p - custom; if (iwe.u.data.length) - start = iwe_stream_add_point(start, stop, &iwe, custom); + start = iwe_stream_add_point(info, start, stop, + &iwe, custom); } out: @@ -1036,7 +1038,7 @@ int lbs_get_scan(struct net_device *dev, struct iw_request_info *info, } /* Translate to WE format this entry */ - next_ev = lbs_translate_scan(priv, ev, stop, iter_bss); + next_ev = lbs_translate_scan(priv, info, ev, stop, iter_bss); if (next_ev == NULL) continue; ev = next_ev; diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c index 6d13a0d15a0c..b047306bf386 100644 --- a/drivers/net/wireless/orinoco.c +++ b/drivers/net/wireless/orinoco.c @@ -4046,6 +4046,7 @@ static int orinoco_ioctl_setscan(struct net_device *dev, * format that the Wireless Tools will understand - Jean II * Return message length or -errno for fatal errors */ static inline char *orinoco_translate_scan(struct net_device *dev, + struct iw_request_info *info, char *current_ev, char *end_buf, union hermes_scan_info *bss, @@ -4062,7 +4063,8 @@ static inline char *orinoco_translate_scan(struct net_device *dev, iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, bss->a.bssid, ETH_ALEN); - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_ADDR_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_ADDR_LEN); /* Other entries will be displayed in the order we give them */ @@ -4072,7 +4074,8 @@ static inline char *orinoco_translate_scan(struct net_device *dev, iwe.u.data.length = 32; iwe.cmd = SIOCGIWESSID; iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, bss->a.essid); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss->a.essid); /* Add mode */ iwe.cmd = SIOCGIWMODE; @@ -4082,7 +4085,8 @@ static inline char *orinoco_translate_scan(struct net_device *dev, iwe.u.mode = IW_MODE_MASTER; else iwe.u.mode = IW_MODE_ADHOC; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_UINT_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_UINT_LEN); } channel = bss->s.channel; @@ -4091,7 +4095,7 @@ static inline char *orinoco_translate_scan(struct net_device *dev, iwe.cmd = SIOCGIWFREQ; iwe.u.freq.m = channel_frequency[channel-1] * 100000; iwe.u.freq.e = 1; - current_ev = iwe_stream_add_event(current_ev, end_buf, + current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_FREQ_LEN); } @@ -4106,7 +4110,8 @@ static inline char *orinoco_translate_scan(struct net_device *dev, iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise; else iwe.u.qual.qual = 0; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_QUAL_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_QUAL_LEN); /* Add encryption capability */ iwe.cmd = SIOCGIWENCODE; @@ -4115,7 +4120,8 @@ static inline char *orinoco_translate_scan(struct net_device *dev, else iwe.u.data.flags = IW_ENCODE_DISABLED; iwe.u.data.length = 0; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, bss->a.essid); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss->a.essid); /* Add EXTRA: Age to display seconds since last beacon/probe response * for given network. */ @@ -4126,11 +4132,12 @@ static inline char *orinoco_translate_scan(struct net_device *dev, jiffies_to_msecs(jiffies - last_scanned)); iwe.u.data.length = p - custom; if (iwe.u.data.length) - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, custom); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, custom); /* Bit rate is not available in Lucent/Agere firmwares */ if (priv->firmware_type != FIRMWARE_TYPE_AGERE) { - char *current_val = current_ev + IW_EV_LCP_LEN; + char *current_val = current_ev + iwe_stream_lcp_len(info); int i; int step; @@ -4149,12 +4156,13 @@ static inline char *orinoco_translate_scan(struct net_device *dev, break; /* Bit rate given in 500 kb/s units (+ 0x80) */ iwe.u.bitrate.value = ((bss->p.rates[i] & 0x7f) * 500000); - current_val = iwe_stream_add_value(current_ev, current_val, + current_val = iwe_stream_add_value(info, current_ev, + current_val, end_buf, &iwe, IW_EV_PARAM_LEN); } /* Check if we added any event */ - if ((current_val - current_ev) > IW_EV_LCP_LEN) + if ((current_val - current_ev) > iwe_stream_lcp_len(info)) current_ev = current_val; } @@ -4190,7 +4198,7 @@ static int orinoco_ioctl_getscan(struct net_device *dev, list_for_each_entry(bss, &priv->bss_list, list) { /* Translate to WE format this entry */ - current_ev = orinoco_translate_scan(dev, current_ev, + current_ev = orinoco_translate_scan(dev, info, current_ev, extra + srq->length, &bss->bss, bss->last_scanned); diff --git a/drivers/net/wireless/prism54/isl_ioctl.c b/drivers/net/wireless/prism54/isl_ioctl.c index 5b375b289036..97fa14e0a479 100644 --- a/drivers/net/wireless/prism54/isl_ioctl.c +++ b/drivers/net/wireless/prism54/isl_ioctl.c @@ -571,8 +571,9 @@ prism54_set_scan(struct net_device *dev, struct iw_request_info *info, */ static char * -prism54_translate_bss(struct net_device *ndev, char *current_ev, - char *end_buf, struct obj_bss *bss, char noise) +prism54_translate_bss(struct net_device *ndev, struct iw_request_info *info, + char *current_ev, char *end_buf, struct obj_bss *bss, + char noise) { struct iw_event iwe; /* Temporary buffer */ short cap; @@ -584,8 +585,8 @@ prism54_translate_bss(struct net_device *ndev, char *current_ev, memcpy(iwe.u.ap_addr.sa_data, bss->address, 6); iwe.u.ap_addr.sa_family = ARPHRD_ETHER; iwe.cmd = SIOCGIWAP; - current_ev = - iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_ADDR_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_ADDR_LEN); /* The following entries will be displayed in the same order we give them */ @@ -593,7 +594,7 @@ prism54_translate_bss(struct net_device *ndev, char *current_ev, iwe.u.data.length = bss->ssid.length; iwe.u.data.flags = 1; iwe.cmd = SIOCGIWESSID; - current_ev = iwe_stream_add_point(current_ev, end_buf, + current_ev = iwe_stream_add_point(info, current_ev, end_buf, &iwe, bss->ssid.octets); /* Capabilities */ @@ -610,9 +611,8 @@ prism54_translate_bss(struct net_device *ndev, char *current_ev, iwe.u.mode = IW_MODE_ADHOC; iwe.cmd = SIOCGIWMODE; if (iwe.u.mode) - current_ev = - iwe_stream_add_event(current_ev, end_buf, &iwe, - IW_EV_UINT_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_UINT_LEN); /* Encryption capability */ if (cap & CAP_CRYPT) @@ -621,14 +621,15 @@ prism54_translate_bss(struct net_device *ndev, char *current_ev, iwe.u.data.flags = IW_ENCODE_DISABLED; iwe.u.data.length = 0; iwe.cmd = SIOCGIWENCODE; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, NULL); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, NULL); /* Add frequency. (short) bss->channel is the frequency in MHz */ iwe.u.freq.m = bss->channel; iwe.u.freq.e = 6; iwe.cmd = SIOCGIWFREQ; - current_ev = - iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_FREQ_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_FREQ_LEN); /* Add quality statistics */ iwe.u.qual.level = bss->rssi; @@ -636,20 +637,20 @@ prism54_translate_bss(struct net_device *ndev, char *current_ev, /* do a simple SNR for quality */ iwe.u.qual.qual = bss->rssi - noise; iwe.cmd = IWEVQUAL; - current_ev = - iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_QUAL_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_QUAL_LEN); /* Add WPA/RSN Information Element, if any */ wpa_ie_len = prism54_wpa_bss_ie_get(priv, bss->address, wpa_ie); if (wpa_ie_len > 0) { iwe.cmd = IWEVGENIE; iwe.u.data.length = min(wpa_ie_len, (size_t)MAX_WPA_IE_LEN); - current_ev = iwe_stream_add_point(current_ev, end_buf, - &iwe, wpa_ie); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, wpa_ie); } /* Do the bitrates */ { - char * current_val = current_ev + IW_EV_LCP_LEN; + char *current_val = current_ev + iwe_stream_lcp_len(info); int i; int mask; @@ -662,14 +663,14 @@ prism54_translate_bss(struct net_device *ndev, char *current_ev, for(i = 0; i < sizeof(scan_rate_list); i++) { if(bss->rates & mask) { iwe.u.bitrate.value = (scan_rate_list[i] * 500000); - current_val = iwe_stream_add_value(current_ev, current_val, - end_buf, &iwe, - IW_EV_PARAM_LEN); + current_val = iwe_stream_add_value( + info, current_ev, current_val, + end_buf, &iwe, IW_EV_PARAM_LEN); } mask <<= 1; } /* Check if we added any event */ - if ((current_val - current_ev) > IW_EV_LCP_LEN) + if ((current_val - current_ev) > iwe_stream_lcp_len(info)) current_ev = current_val; } @@ -710,7 +711,7 @@ prism54_get_scan(struct net_device *ndev, struct iw_request_info *info, /* ok now, scan the list and translate its info */ for (i = 0; i < (int) bsslist->nr; i++) { - current_ev = prism54_translate_bss(ndev, current_ev, + current_ev = prism54_translate_bss(ndev, info, current_ev, extra + dwrq->length, &(bsslist->bsslist[i]), noise); @@ -2704,6 +2705,7 @@ prism2_ioctl_scan_req(struct net_device *ndev, struct prism2_hostapd_param *param) { islpci_private *priv = netdev_priv(ndev); + struct iw_request_info info; int i, rvalue; struct obj_bsslist *bsslist; u32 noise = 0; @@ -2727,9 +2729,12 @@ prism2_ioctl_scan_req(struct net_device *ndev, rvalue |= mgt_get_request(priv, DOT11_OID_BSSLIST, 0, NULL, &r); bsslist = r.ptr; + info.cmd = PRISM54_HOSTAPD; + info.flags = 0; + /* ok now, scan the list and translate its info */ for (i = 0; i < min(IW_MAX_AP, (int) bsslist->nr); i++) - current_ev = prism54_translate_bss(ndev, current_ev, + current_ev = prism54_translate_bss(ndev, &info, current_ev, extra + IW_SCAN_MAX_DATA, &(bsslist->bsslist[i]), noise); diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index a36d2c85e26e..65c50025c88f 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -1648,7 +1648,9 @@ static int rndis_iw_set_scan(struct net_device *dev, static char *rndis_translate_scan(struct net_device *dev, - char *cev, char *end_buf, struct ndis_80211_bssid_ex *bssid) + struct iw_request_info *info, char *cev, + char *end_buf, + struct ndis_80211_bssid_ex *bssid) { #ifdef DEBUG struct usbnet *usbdev = dev->priv; @@ -1667,14 +1669,14 @@ static char *rndis_translate_scan(struct net_device *dev, iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, bssid->mac, ETH_ALEN); - cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_ADDR_LEN); + cev = iwe_stream_add_event(info, cev, end_buf, &iwe, IW_EV_ADDR_LEN); devdbg(usbdev, "SSID(%d) %s", le32_to_cpu(bssid->ssid.length), bssid->ssid.essid); iwe.cmd = SIOCGIWESSID; iwe.u.essid.length = le32_to_cpu(bssid->ssid.length); iwe.u.essid.flags = 1; - cev = iwe_stream_add_point(cev, end_buf, &iwe, bssid->ssid.essid); + cev = iwe_stream_add_point(info, cev, end_buf, &iwe, bssid->ssid.essid); devdbg(usbdev, "MODE %d", le32_to_cpu(bssid->net_infra)); iwe.cmd = SIOCGIWMODE; @@ -1690,12 +1692,12 @@ static char *rndis_translate_scan(struct net_device *dev, iwe.u.mode = IW_MODE_AUTO; break; } - cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_UINT_LEN); + cev = iwe_stream_add_event(info, cev, end_buf, &iwe, IW_EV_UINT_LEN); devdbg(usbdev, "FREQ %d kHz", le32_to_cpu(bssid->config.ds_config)); iwe.cmd = SIOCGIWFREQ; dsconfig_to_freq(le32_to_cpu(bssid->config.ds_config), &iwe.u.freq); - cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_FREQ_LEN); + cev = iwe_stream_add_event(info, cev, end_buf, &iwe, IW_EV_FREQ_LEN); devdbg(usbdev, "QUAL %d", le32_to_cpu(bssid->rssi)); iwe.cmd = IWEVQUAL; @@ -1704,7 +1706,7 @@ static char *rndis_translate_scan(struct net_device *dev, iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_INVALID; - cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_QUAL_LEN); + cev = iwe_stream_add_event(info, cev, end_buf, &iwe, IW_EV_QUAL_LEN); devdbg(usbdev, "ENCODE %d", le32_to_cpu(bssid->privacy)); iwe.cmd = SIOCGIWENCODE; @@ -1714,10 +1716,10 @@ static char *rndis_translate_scan(struct net_device *dev, else iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; - cev = iwe_stream_add_point(cev, end_buf, &iwe, NULL); + cev = iwe_stream_add_point(info, cev, end_buf, &iwe, NULL); devdbg(usbdev, "RATES:"); - current_val = cev + IW_EV_LCP_LEN; + current_val = cev + iwe_stream_lcp_len(info); iwe.cmd = SIOCGIWRATE; for (i = 0; i < sizeof(bssid->rates); i++) { if (bssid->rates[i] & 0x7f) { @@ -1725,13 +1727,13 @@ static char *rndis_translate_scan(struct net_device *dev, ((bssid->rates[i] & 0x7f) * 500000); devdbg(usbdev, " %d", iwe.u.bitrate.value); - current_val = iwe_stream_add_value(cev, + current_val = iwe_stream_add_value(info, cev, current_val, end_buf, &iwe, IW_EV_PARAM_LEN); } } - if ((current_val - cev) > IW_EV_LCP_LEN) + if ((current_val - cev) > iwe_stream_lcp_len(info)) cev = current_val; beacon = le32_to_cpu(bssid->config.beacon_period); @@ -1739,14 +1741,14 @@ static char *rndis_translate_scan(struct net_device *dev, iwe.cmd = IWEVCUSTOM; snprintf(sbuf, sizeof(sbuf), "bcn_int=%d", beacon); iwe.u.data.length = strlen(sbuf); - cev = iwe_stream_add_point(cev, end_buf, &iwe, sbuf); + cev = iwe_stream_add_point(info, cev, end_buf, &iwe, sbuf); atim = le32_to_cpu(bssid->config.atim_window); devdbg(usbdev, "ATIM %d", atim); iwe.cmd = IWEVCUSTOM; snprintf(sbuf, sizeof(sbuf), "atim=%u", atim); iwe.u.data.length = strlen(sbuf); - cev = iwe_stream_add_point(cev, end_buf, &iwe, sbuf); + cev = iwe_stream_add_point(info, cev, end_buf, &iwe, sbuf); ie = (void *)(bssid->ies + sizeof(struct ndis_80211_fixed_ies)); ie_len = min(bssid_len - (int)sizeof(*bssid), @@ -1760,7 +1762,7 @@ static char *rndis_translate_scan(struct net_device *dev, (ie->id == MFIE_TYPE_RSN) ? 2 : 1); iwe.cmd = IWEVGENIE; iwe.u.data.length = min(ie->len + 2, MAX_WPA_IE_LEN); - cev = iwe_stream_add_point(cev, end_buf, &iwe, + cev = iwe_stream_add_point(info, cev, end_buf, &iwe, (u8 *)ie); } @@ -1803,8 +1805,8 @@ static int rndis_iw_get_scan(struct net_device *dev, devdbg(usbdev, "SIOCGIWSCAN: %d BSSIDs found", count); while (count && ((void *)bssid + bssid_len) <= (buf + len)) { - cev = rndis_translate_scan(dev, cev, extra + IW_SCAN_MAX_DATA, - bssid); + cev = rndis_translate_scan(dev, info, cev, + extra + IW_SCAN_MAX_DATA, bssid); bssid = (void *)bssid + bssid_len; bssid_len = le32_to_cpu(bssid->length); count--; diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c index 42a36b3f3ff7..377141995e36 100644 --- a/drivers/net/wireless/wl3501_cs.c +++ b/drivers/net/wireless/wl3501_cs.c @@ -1624,25 +1624,25 @@ static int wl3501_get_scan(struct net_device *dev, struct iw_request_info *info, iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, this->bss_set[i].bssid, ETH_ALEN); - current_ev = iwe_stream_add_event(current_ev, + current_ev = iwe_stream_add_event(info, current_ev, extra + IW_SCAN_MAX_DATA, &iwe, IW_EV_ADDR_LEN); iwe.cmd = SIOCGIWESSID; iwe.u.data.flags = 1; iwe.u.data.length = this->bss_set[i].ssid.el.len; - current_ev = iwe_stream_add_point(current_ev, + current_ev = iwe_stream_add_point(info, current_ev, extra + IW_SCAN_MAX_DATA, &iwe, this->bss_set[i].ssid.essid); iwe.cmd = SIOCGIWMODE; iwe.u.mode = this->bss_set[i].bss_type; - current_ev = iwe_stream_add_event(current_ev, + current_ev = iwe_stream_add_event(info, current_ev, extra + IW_SCAN_MAX_DATA, &iwe, IW_EV_UINT_LEN); iwe.cmd = SIOCGIWFREQ; iwe.u.freq.m = this->bss_set[i].ds_pset.chan; iwe.u.freq.e = 0; - current_ev = iwe_stream_add_event(current_ev, + current_ev = iwe_stream_add_event(info, current_ev, extra + IW_SCAN_MAX_DATA, &iwe, IW_EV_FREQ_LEN); iwe.cmd = SIOCGIWENCODE; @@ -1651,7 +1651,7 @@ static int wl3501_get_scan(struct net_device *dev, struct iw_request_info *info, else iwe.u.data.flags = IW_ENCODE_DISABLED; iwe.u.data.length = 0; - current_ev = iwe_stream_add_point(current_ev, + current_ev = iwe_stream_add_point(info, current_ev, extra + IW_SCAN_MAX_DATA, &iwe, NULL); } diff --git a/drivers/net/wireless/zd1201.c b/drivers/net/wireless/zd1201.c index d5c0c66188ca..07e4d1f73207 100644 --- a/drivers/net/wireless/zd1201.c +++ b/drivers/net/wireless/zd1201.c @@ -1152,32 +1152,36 @@ static int zd1201_get_scan(struct net_device *dev, iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, zd->rxdata+i+6, 6); - cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_ADDR_LEN); + cev = iwe_stream_add_event(info, cev, end_buf, + &iwe, IW_EV_ADDR_LEN); iwe.cmd = SIOCGIWESSID; iwe.u.data.length = zd->rxdata[i+16]; iwe.u.data.flags = 1; - cev = iwe_stream_add_point(cev, end_buf, &iwe, zd->rxdata+i+18); + cev = iwe_stream_add_point(info, cev, end_buf, + &iwe, zd->rxdata+i+18); iwe.cmd = SIOCGIWMODE; if (zd->rxdata[i+14]&0x01) iwe.u.mode = IW_MODE_MASTER; else iwe.u.mode = IW_MODE_ADHOC; - cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_UINT_LEN); + cev = iwe_stream_add_event(info, cev, end_buf, + &iwe, IW_EV_UINT_LEN); iwe.cmd = SIOCGIWFREQ; iwe.u.freq.m = zd->rxdata[i+0]; iwe.u.freq.e = 0; - cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_FREQ_LEN); + cev = iwe_stream_add_event(info, cev, end_buf, + &iwe, IW_EV_FREQ_LEN); iwe.cmd = SIOCGIWRATE; iwe.u.bitrate.fixed = 0; iwe.u.bitrate.disabled = 0; for (j=0; j<10; j++) if (zd->rxdata[i+50+j]) { iwe.u.bitrate.value = (zd->rxdata[i+50+j]&0x7f)*500000; - cev=iwe_stream_add_event(cev, end_buf, &iwe, - IW_EV_PARAM_LEN); + cev = iwe_stream_add_event(info, cev, end_buf, + &iwe, IW_EV_PARAM_LEN); } iwe.cmd = SIOCGIWENCODE; @@ -1186,14 +1190,15 @@ static int zd1201_get_scan(struct net_device *dev, iwe.u.data.flags = IW_ENCODE_ENABLED; else iwe.u.data.flags = IW_ENCODE_DISABLED; - cev = iwe_stream_add_point(cev, end_buf, &iwe, NULL); + cev = iwe_stream_add_point(info, cev, end_buf, &iwe, NULL); iwe.cmd = IWEVQUAL; iwe.u.qual.qual = zd->rxdata[i+4]; iwe.u.qual.noise= zd->rxdata[i+2]/10-100; iwe.u.qual.level = (256+zd->rxdata[i+4]*100)/255-100; iwe.u.qual.updated = 7; - cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_QUAL_LEN); + cev = iwe_stream_add_event(info, cev, end_buf, + &iwe, IW_EV_QUAL_LEN); } if (!enabled_save) diff --git a/include/linux/wireless.h b/include/linux/wireless.h index 79d846875825..d7958f9b52cb 100644 --- a/include/linux/wireless.h +++ b/include/linux/wireless.h @@ -1113,6 +1113,21 @@ struct iw_event #define IW_EV_POINT_LEN (IW_EV_LCP_LEN + sizeof(struct iw_point) - \ IW_EV_POINT_OFF) +#ifdef __KERNEL__ +#ifdef CONFIG_COMPAT +struct __compat_iw_event { + __u16 len; /* Real length of this stuff */ + __u16 cmd; /* Wireless IOCTL */ + compat_caddr_t pointer; +}; +#define IW_EV_COMPAT_LCP_LEN offsetof(struct __compat_iw_event, pointer) +#define IW_EV_COMPAT_POINT_OFF offsetof(struct compat_iw_point, length) +#define IW_EV_COMPAT_POINT_LEN \ + (IW_EV_COMPAT_LCP_LEN + sizeof(struct compat_iw_point) - \ + IW_EV_COMPAT_POINT_OFF) +#endif +#endif + /* Size of the Event prefix when packed in stream */ #define IW_EV_LCP_PK_LEN (4) /* Size of the various events when packed in stream */ diff --git a/include/net/iw_handler.h b/include/net/iw_handler.h index c99a8eec84e7..51b9a37de991 100644 --- a/include/net/iw_handler.h +++ b/include/net/iw_handler.h @@ -478,105 +478,58 @@ extern void wireless_spy_update(struct net_device * dev, * Function that are so simple that it's more efficient inlining them */ -/*------------------------------------------------------------------*/ -/* - * Wrapper to add an Wireless Event to a stream of events. - */ -static inline char * -iwe_stream_add_event(char * stream, /* Stream of events */ - char * ends, /* End of stream */ - struct iw_event *iwe, /* Payload */ - int event_len) /* Real size of payload */ +static inline int iwe_stream_lcp_len(struct iw_request_info *info) { - /* Check if it's possible */ - if(likely((stream + event_len) < ends)) { - iwe->len = event_len; - /* Beware of alignement issues on 64 bits */ - memcpy(stream, (char *) iwe, IW_EV_LCP_PK_LEN); - memcpy(stream + IW_EV_LCP_LEN, - ((char *) iwe) + IW_EV_LCP_LEN, - event_len - IW_EV_LCP_LEN); - stream += event_len; - } - return stream; +#ifdef CONFIG_COMPAT + if (info->flags & IW_REQUEST_FLAG_COMPAT) + return IW_EV_COMPAT_LCP_LEN; +#endif + return IW_EV_LCP_LEN; } -/*------------------------------------------------------------------*/ -/* - * Wrapper to add an short Wireless Event containing a pointer to a - * stream of events. - */ -static inline char * -iwe_stream_add_point(char * stream, /* Stream of events */ - char * ends, /* End of stream */ - struct iw_event *iwe, /* Payload length + flags */ - char * extra) /* More payload */ +static inline int iwe_stream_point_len(struct iw_request_info *info) { - int event_len = IW_EV_POINT_LEN + iwe->u.data.length; - /* Check if it's possible */ - if(likely((stream + event_len) < ends)) { - iwe->len = event_len; - memcpy(stream, (char *) iwe, IW_EV_LCP_PK_LEN); - memcpy(stream + IW_EV_LCP_LEN, - ((char *) iwe) + IW_EV_LCP_LEN + IW_EV_POINT_OFF, - IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN); - memcpy(stream + IW_EV_POINT_LEN, extra, iwe->u.data.length); - stream += event_len; - } - return stream; +#ifdef CONFIG_COMPAT + if (info->flags & IW_REQUEST_FLAG_COMPAT) + return IW_EV_COMPAT_POINT_LEN; +#endif + return IW_EV_POINT_LEN; } -/*------------------------------------------------------------------*/ -/* - * Wrapper to add a value to a Wireless Event in a stream of events. - * Be careful, this one is tricky to use properly : - * At the first run, you need to have (value = event + IW_EV_LCP_LEN). - */ -static inline char * -iwe_stream_add_value(char * event, /* Event in the stream */ - char * value, /* Value in event */ - char * ends, /* End of stream */ - struct iw_event *iwe, /* Payload */ - int event_len) /* Real size of payload */ +static inline int iwe_stream_event_len_adjust(struct iw_request_info *info, + int event_len) { - /* Don't duplicate LCP */ - event_len -= IW_EV_LCP_LEN; - - /* Check if it's possible */ - if(likely((value + event_len) < ends)) { - /* Add new value */ - memcpy(value, (char *) iwe + IW_EV_LCP_LEN, event_len); - value += event_len; - /* Patch LCP */ - iwe->len = value - event; - memcpy(event, (char *) iwe, IW_EV_LCP_LEN); +#ifdef CONFIG_COMPAT + if (info->flags & IW_REQUEST_FLAG_COMPAT) { + event_len -= IW_EV_LCP_LEN; + event_len += IW_EV_COMPAT_LCP_LEN; } - return value; +#endif + + return event_len; } /*------------------------------------------------------------------*/ /* * Wrapper to add an Wireless Event to a stream of events. - * Same as above, with explicit error check... */ static inline char * -iwe_stream_check_add_event(char * stream, /* Stream of events */ - char * ends, /* End of stream */ - struct iw_event *iwe, /* Payload */ - int event_len, /* Size of payload */ - int * perr) /* Error report */ +iwe_stream_add_event(struct iw_request_info *info, char *stream, char *ends, + struct iw_event *iwe, int event_len) { - /* Check if it's possible, set error if not */ + int lcp_len = iwe_stream_lcp_len(info); + + event_len = iwe_stream_event_len_adjust(info, event_len); + + /* Check if it's possible */ if(likely((stream + event_len) < ends)) { iwe->len = event_len; /* Beware of alignement issues on 64 bits */ memcpy(stream, (char *) iwe, IW_EV_LCP_PK_LEN); - memcpy(stream + IW_EV_LCP_LEN, - ((char *) iwe) + IW_EV_LCP_LEN, - event_len - IW_EV_LCP_LEN); + memcpy(stream + lcp_len, &iwe->u, + event_len - lcp_len); stream += event_len; - } else - *perr = -E2BIG; + } return stream; } @@ -584,27 +537,25 @@ iwe_stream_check_add_event(char * stream, /* Stream of events */ /* * Wrapper to add an short Wireless Event containing a pointer to a * stream of events. - * Same as above, with explicit error check... */ static inline char * -iwe_stream_check_add_point(char * stream, /* Stream of events */ - char * ends, /* End of stream */ - struct iw_event *iwe, /* Payload length + flags */ - char * extra, /* More payload */ - int * perr) /* Error report */ +iwe_stream_add_point(struct iw_request_info *info, char *stream, char *ends, + struct iw_event *iwe, char *extra) { - int event_len = IW_EV_POINT_LEN + iwe->u.data.length; + int event_len = iwe_stream_point_len(info) + iwe->u.data.length; + int point_len = iwe_stream_point_len(info); + int lcp_len = iwe_stream_lcp_len(info); + /* Check if it's possible */ if(likely((stream + event_len) < ends)) { iwe->len = event_len; memcpy(stream, (char *) iwe, IW_EV_LCP_PK_LEN); - memcpy(stream + IW_EV_LCP_LEN, - ((char *) iwe) + IW_EV_LCP_LEN + IW_EV_POINT_OFF, + memcpy(stream + lcp_len, + ((char *) &iwe->u) + IW_EV_POINT_OFF, IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN); - memcpy(stream + IW_EV_POINT_LEN, extra, iwe->u.data.length); + memcpy(stream + point_len, extra, iwe->u.data.length); stream += event_len; - } else - *perr = -E2BIG; + } return stream; } @@ -613,29 +564,25 @@ iwe_stream_check_add_point(char * stream, /* Stream of events */ * Wrapper to add a value to a Wireless Event in a stream of events. * Be careful, this one is tricky to use properly : * At the first run, you need to have (value = event + IW_EV_LCP_LEN). - * Same as above, with explicit error check... */ static inline char * -iwe_stream_check_add_value(char * event, /* Event in the stream */ - char * value, /* Value in event */ - char * ends, /* End of stream */ - struct iw_event *iwe, /* Payload */ - int event_len, /* Size of payload */ - int * perr) /* Error report */ +iwe_stream_add_value(struct iw_request_info *info, char *event, char *value, + char *ends, struct iw_event *iwe, int event_len) { + int lcp_len = iwe_stream_lcp_len(info); + /* Don't duplicate LCP */ event_len -= IW_EV_LCP_LEN; /* Check if it's possible */ if(likely((value + event_len) < ends)) { /* Add new value */ - memcpy(value, (char *) iwe + IW_EV_LCP_LEN, event_len); + memcpy(value, &iwe->u, event_len); value += event_len; /* Patch LCP */ iwe->len = value - event; - memcpy(event, (char *) iwe, IW_EV_LCP_LEN); - } else - *perr = -E2BIG; + memcpy(event, (char *) iwe, lcp_len); + } return value; } diff --git a/net/ieee80211/ieee80211_wx.c b/net/ieee80211/ieee80211_wx.c index 822606b615ca..973832dd7faf 100644 --- a/net/ieee80211/ieee80211_wx.c +++ b/net/ieee80211/ieee80211_wx.c @@ -43,8 +43,9 @@ static const char *ieee80211_modes[] = { #define MAX_CUSTOM_LEN 64 static char *ieee80211_translate_scan(struct ieee80211_device *ieee, - char *start, char *stop, - struct ieee80211_network *network) + char *start, char *stop, + struct ieee80211_network *network, + struct iw_request_info *info) { char custom[MAX_CUSTOM_LEN]; char *p; @@ -57,7 +58,7 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN); - start = iwe_stream_add_event(start, stop, &iwe, IW_EV_ADDR_LEN); + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN); /* Remaining entries will be displayed in the order we provide them */ @@ -66,17 +67,19 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, iwe.u.data.flags = 1; if (network->flags & NETWORK_EMPTY_ESSID) { iwe.u.data.length = sizeof(""); - start = iwe_stream_add_point(start, stop, &iwe, ""); + start = iwe_stream_add_point(info, start, stop, + &iwe, ""); } else { iwe.u.data.length = min(network->ssid_len, (u8) 32); - start = iwe_stream_add_point(start, stop, &iwe, network->ssid); + start = iwe_stream_add_point(info, start, stop, + &iwe, network->ssid); } /* Add the protocol name */ iwe.cmd = SIOCGIWNAME; snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s", ieee80211_modes[network->mode]); - start = iwe_stream_add_event(start, stop, &iwe, IW_EV_CHAR_LEN); + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN); /* Add mode */ iwe.cmd = SIOCGIWMODE; @@ -86,7 +89,8 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, else iwe.u.mode = IW_MODE_ADHOC; - start = iwe_stream_add_event(start, stop, &iwe, IW_EV_UINT_LEN); + start = iwe_stream_add_event(info, start, stop, + &iwe, IW_EV_UINT_LEN); } /* Add channel and frequency */ @@ -95,7 +99,7 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, iwe.u.freq.m = ieee80211_channel_to_freq(ieee, network->channel); iwe.u.freq.e = 6; iwe.u.freq.i = 0; - start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN); + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN); /* Add encryption capability */ iwe.cmd = SIOCGIWENCODE; @@ -104,12 +108,13 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, else iwe.u.data.flags = IW_ENCODE_DISABLED; iwe.u.data.length = 0; - start = iwe_stream_add_point(start, stop, &iwe, network->ssid); + start = iwe_stream_add_point(info, start, stop, + &iwe, network->ssid); /* Add basic and extended rates */ /* Rate : stuffing multiple values in a single event require a bit * more of magic - Jean II */ - current_val = start + IW_EV_LCP_LEN; + current_val = start + iwe_stream_lcp_len(info); iwe.cmd = SIOCGIWRATE; /* Those two flags are ignored... */ iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; @@ -124,17 +129,19 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, /* Bit rate given in 500 kb/s units (+ 0x80) */ iwe.u.bitrate.value = ((rate & 0x7f) * 500000); /* Add new value to event */ - current_val = iwe_stream_add_value(start, current_val, stop, &iwe, IW_EV_PARAM_LEN); + current_val = iwe_stream_add_value(info, start, current_val, + stop, &iwe, IW_EV_PARAM_LEN); } for (; j < network->rates_ex_len; j++) { rate = network->rates_ex[j] & 0x7F; /* Bit rate given in 500 kb/s units (+ 0x80) */ iwe.u.bitrate.value = ((rate & 0x7f) * 500000); /* Add new value to event */ - current_val = iwe_stream_add_value(start, current_val, stop, &iwe, IW_EV_PARAM_LEN); + current_val = iwe_stream_add_value(info, start, current_val, + stop, &iwe, IW_EV_PARAM_LEN); } /* Check if we added any rate */ - if((current_val - start) > IW_EV_LCP_LEN) + if ((current_val - start) > iwe_stream_lcp_len(info)) start = current_val; /* Add quality statistics */ @@ -181,14 +188,14 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, iwe.u.qual.level = network->stats.signal; } - start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN); + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN); iwe.cmd = IWEVCUSTOM; p = custom; iwe.u.data.length = p - custom; if (iwe.u.data.length) - start = iwe_stream_add_point(start, stop, &iwe, custom); + start = iwe_stream_add_point(info, start, stop, &iwe, custom); memset(&iwe, 0, sizeof(iwe)); if (network->wpa_ie_len) { @@ -196,7 +203,7 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, memcpy(buf, network->wpa_ie, network->wpa_ie_len); iwe.cmd = IWEVGENIE; iwe.u.data.length = network->wpa_ie_len; - start = iwe_stream_add_point(start, stop, &iwe, buf); + start = iwe_stream_add_point(info, start, stop, &iwe, buf); } memset(&iwe, 0, sizeof(iwe)); @@ -205,7 +212,7 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, memcpy(buf, network->rsn_ie, network->rsn_ie_len); iwe.cmd = IWEVGENIE; iwe.u.data.length = network->rsn_ie_len; - start = iwe_stream_add_point(start, stop, &iwe, buf); + start = iwe_stream_add_point(info, start, stop, &iwe, buf); } /* Add EXTRA: Age to display seconds since last beacon/probe response @@ -217,7 +224,7 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, jiffies_to_msecs(jiffies - network->last_scanned)); iwe.u.data.length = p - custom; if (iwe.u.data.length) - start = iwe_stream_add_point(start, stop, &iwe, custom); + start = iwe_stream_add_point(info, start, stop, &iwe, custom); /* Add spectrum management information */ iwe.cmd = -1; @@ -238,7 +245,7 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, if (iwe.cmd == IWEVCUSTOM) { iwe.u.data.length = p - custom; - start = iwe_stream_add_point(start, stop, &iwe, custom); + start = iwe_stream_add_point(info, start, stop, &iwe, custom); } return start; @@ -272,7 +279,8 @@ int ieee80211_wx_get_scan(struct ieee80211_device *ieee, if (ieee->scan_age == 0 || time_after(network->last_scanned + ieee->scan_age, jiffies)) - ev = ieee80211_translate_scan(ieee, ev, stop, network); + ev = ieee80211_translate_scan(ieee, ev, stop, network, + info); else IEEE80211_DEBUG_SCAN("Not showing network '%s (" "%s)' due to age (%dms).\n", diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 14fccf16b80f..80a9e7c07b47 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -24,6 +24,7 @@ #include #include #include +#include #include "key.h" #include "sta_info.h" @@ -867,7 +868,9 @@ int ieee80211_sta_set_bssid(struct net_device *dev, u8 *bssid); int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len); void ieee80211_sta_req_auth(struct net_device *dev, struct ieee80211_if_sta *ifsta); -int ieee80211_sta_scan_results(struct net_device *dev, char *buf, size_t len); +int ieee80211_sta_scan_results(struct net_device *dev, + struct iw_request_info *info, + char *buf, size_t len); ieee80211_rx_result ieee80211_sta_rx_scan( struct net_device *dev, struct sk_buff *skb, struct ieee80211_rx_status *rx_status); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 55659a730dc1..e06d6450f215 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -4087,6 +4087,7 @@ int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len) static char * ieee80211_sta_scan_result(struct net_device *dev, + struct iw_request_info *info, struct ieee80211_sta_bss *bss, char *current_ev, char *end_buf) { @@ -4101,7 +4102,7 @@ ieee80211_sta_scan_result(struct net_device *dev, iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN); - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_ADDR_LEN); memset(&iwe, 0, sizeof(iwe)); @@ -4109,13 +4110,13 @@ ieee80211_sta_scan_result(struct net_device *dev, if (bss_mesh_cfg(bss)) { iwe.u.data.length = bss_mesh_id_len(bss); iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, - bss_mesh_id(bss)); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss_mesh_id(bss)); } else { iwe.u.data.length = bss->ssid_len; iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, - bss->ssid); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss->ssid); } if (bss->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS) @@ -4128,22 +4129,22 @@ ieee80211_sta_scan_result(struct net_device *dev, iwe.u.mode = IW_MODE_MASTER; else iwe.u.mode = IW_MODE_ADHOC; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, - IW_EV_UINT_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_UINT_LEN); } memset(&iwe, 0, sizeof(iwe)); iwe.cmd = SIOCGIWFREQ; iwe.u.freq.m = ieee80211_frequency_to_channel(bss->freq); iwe.u.freq.e = 0; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_FREQ_LEN); memset(&iwe, 0, sizeof(iwe)); iwe.cmd = SIOCGIWFREQ; iwe.u.freq.m = bss->freq; iwe.u.freq.e = 6; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_FREQ_LEN); memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVQUAL; @@ -4151,7 +4152,7 @@ ieee80211_sta_scan_result(struct net_device *dev, iwe.u.qual.level = bss->signal; iwe.u.qual.noise = bss->noise; iwe.u.qual.updated = local->wstats_flags; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_QUAL_LEN); memset(&iwe, 0, sizeof(iwe)); @@ -4161,35 +4162,36 @@ ieee80211_sta_scan_result(struct net_device *dev, else iwe.u.data.flags = IW_ENCODE_DISABLED; iwe.u.data.length = 0; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, ""); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, ""); if (bss && bss->wpa_ie) { memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVGENIE; iwe.u.data.length = bss->wpa_ie_len; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, - bss->wpa_ie); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss->wpa_ie); } if (bss && bss->rsn_ie) { memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVGENIE; iwe.u.data.length = bss->rsn_ie_len; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, - bss->rsn_ie); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss->rsn_ie); } if (bss && bss->ht_ie) { memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVGENIE; iwe.u.data.length = bss->ht_ie_len; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, - bss->ht_ie); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss->ht_ie); } if (bss && bss->supp_rates_len > 0) { /* display all supported rates in readable format */ - char *p = current_ev + IW_EV_LCP_LEN; + char *p = current_ev + iwe_stream_lcp_len(info); int i; memset(&iwe, 0, sizeof(iwe)); @@ -4200,7 +4202,7 @@ ieee80211_sta_scan_result(struct net_device *dev, for (i = 0; i < bss->supp_rates_len; i++) { iwe.u.bitrate.value = ((bss->supp_rates[i] & 0x7f) * 500000); - p = iwe_stream_add_value(current_ev, p, + p = iwe_stream_add_value(info, current_ev, p, end_buf, &iwe, IW_EV_PARAM_LEN); } current_ev = p; @@ -4214,7 +4216,8 @@ ieee80211_sta_scan_result(struct net_device *dev, iwe.cmd = IWEVCUSTOM; sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->timestamp)); iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, buf); kfree(buf); } @@ -4229,31 +4232,36 @@ ieee80211_sta_scan_result(struct net_device *dev, iwe.cmd = IWEVCUSTOM; sprintf(buf, "Mesh network (version %d)", cfg[0]); iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, buf); sprintf(buf, "Path Selection Protocol ID: " "0x%02X%02X%02X%02X", cfg[1], cfg[2], cfg[3], cfg[4]); iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, buf); sprintf(buf, "Path Selection Metric ID: " "0x%02X%02X%02X%02X", cfg[5], cfg[6], cfg[7], cfg[8]); iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, buf); sprintf(buf, "Congestion Control Mode ID: " "0x%02X%02X%02X%02X", cfg[9], cfg[10], cfg[11], cfg[12]); iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, buf); sprintf(buf, "Channel Precedence: " "0x%02X%02X%02X%02X", cfg[13], cfg[14], cfg[15], cfg[16]); iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, buf); kfree(buf); } @@ -4263,7 +4271,9 @@ ieee80211_sta_scan_result(struct net_device *dev, } -int ieee80211_sta_scan_results(struct net_device *dev, char *buf, size_t len) +int ieee80211_sta_scan_results(struct net_device *dev, + struct iw_request_info *info, + char *buf, size_t len) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); char *current_ev = buf; @@ -4276,8 +4286,8 @@ int ieee80211_sta_scan_results(struct net_device *dev, char *buf, size_t len) spin_unlock_bh(&local->sta_bss_lock); return -E2BIG; } - current_ev = ieee80211_sta_scan_result(dev, bss, current_ev, - end_buf); + current_ev = ieee80211_sta_scan_result(dev, info, bss, + current_ev, end_buf); } spin_unlock_bh(&local->sta_bss_lock); return current_ev - buf; diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index 5af3862e7191..f47d13bdf7f7 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -567,7 +567,7 @@ static int ieee80211_ioctl_giwscan(struct net_device *dev, if (local->sta_sw_scanning || local->sta_hw_scanning) return -EAGAIN; - res = ieee80211_sta_scan_results(dev, extra, data->length); + res = ieee80211_sta_scan_results(dev, info, extra, data->length); if (res >= 0) { data->length = res; return 0; -- cgit v1.2.3 From df3bc8bd8f8fd17e9b22859d82af38fa702e75b7 Mon Sep 17 00:00:00 2001 From: Chas Williams Date: Tue, 17 Jun 2008 16:19:24 -0700 Subject: atm: [suni] add support for setting loopback and framing modes Signed-off-by: Chas Williams Signed-off-by: David S. Miller --- drivers/atm/suni.c | 121 +++++++++++++++++++++++++++++++++++++++++++------- drivers/atm/suni.h | 31 +++++++++++-- include/linux/sonet.h | 2 +- 3 files changed, 133 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/drivers/atm/suni.c b/drivers/atm/suni.c index 6097f6877a44..6dd3f5919968 100644 --- a/drivers/atm/suni.c +++ b/drivers/atm/suni.c @@ -1,8 +1,14 @@ -/* drivers/atm/suni.c - PMC PM5346 SUNI (PHY) driver */ +/* + * drivers/atm/suni.c - S/UNI PHY driver + * + * Supports the following: + * PMC PM5346 S/UNI LITE + * PMC PM5350 S/UNI 155 ULTRA + * PMC PM5355 S/UNI 622 + */ /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ - #include #include #include @@ -146,25 +152,105 @@ static int get_diag(struct atm_dev *dev,void __user *arg) static int set_loopback(struct atm_dev *dev,int mode) { unsigned char control; + int reg, dle, lle; + + if (PRIV(dev)->type == SUNI_MRI_TYPE_PM5355) { + reg = SUNI_MCM; + dle = SUNI_MCM_DLE; + lle = SUNI_MCM_LLE; + } else { + reg = SUNI_MCT; + dle = SUNI_MCT_DLE; + lle = SUNI_MCT_LLE; + } - control = GET(MCT) & ~(SUNI_MCT_DLE | SUNI_MCT_LLE); + control = dev->ops->phy_get(dev, reg) & ~(dle | lle); switch (mode) { case ATM_LM_NONE: break; case ATM_LM_LOC_PHY: - control |= SUNI_MCT_DLE; + control |= dle; break; case ATM_LM_RMT_PHY: - control |= SUNI_MCT_LLE; + control |= lle; break; default: return -EINVAL; } - PUT(control,MCT); + dev->ops->phy_put(dev, control, reg); PRIV(dev)->loop_mode = mode; return 0; } +/* + * SONET vs. SDH Configuration + * + * Z0INS (register 0x06): 0 for SONET, 1 for SDH + * ENSS (register 0x3D): 0 for SONET, 1 for SDH + * LEN16 (register 0x28): 0 for SONET, 1 for SDH (n/a for S/UNI 155 QUAD) + * LEN16 (register 0x50): 0 for SONET, 1 for SDH (n/a for S/UNI 155 QUAD) + * S[1:0] (register 0x46): 00 for SONET, 10 for SDH + */ + +static int set_sonet(struct atm_dev *dev) +{ + if (PRIV(dev)->type == SUNI_MRI_TYPE_PM5355) { + PUT(GET(RPOP_RC) & ~SUNI_RPOP_RC_ENSS, RPOP_RC); + PUT(GET(SSTB_CTRL) & ~SUNI_SSTB_CTRL_LEN16, SSTB_CTRL); + PUT(GET(SPTB_CTRL) & ~SUNI_SPTB_CTRL_LEN16, SPTB_CTRL); + } + + REG_CHANGE(SUNI_TPOP_APM_S, SUNI_TPOP_APM_S_SHIFT, + SUNI_TPOP_S_SONET, TPOP_APM); + + return 0; +} + +static int set_sdh(struct atm_dev *dev) +{ + if (PRIV(dev)->type == SUNI_MRI_TYPE_PM5355) { + PUT(GET(RPOP_RC) | SUNI_RPOP_RC_ENSS, RPOP_RC); + PUT(GET(SSTB_CTRL) | SUNI_SSTB_CTRL_LEN16, SSTB_CTRL); + PUT(GET(SPTB_CTRL) | SUNI_SPTB_CTRL_LEN16, SPTB_CTRL); + } + + REG_CHANGE(SUNI_TPOP_APM_S, SUNI_TPOP_APM_S_SHIFT, + SUNI_TPOP_S_SDH, TPOP_APM); + + return 0; +} + + +static int get_framing(struct atm_dev *dev, void __user *arg) +{ + int framing; + unsigned char s; + + + s = (GET(TPOP_APM) & SUNI_TPOP_APM_S) >> SUNI_TPOP_APM_S_SHIFT; + if (s == SUNI_TPOP_S_SONET) + framing = SONET_FRAME_SONET; + else + framing = SONET_FRAME_SDH; + + return put_user(framing, (int __user *) arg) ? -EFAULT : 0; +} + +static int set_framing(struct atm_dev *dev, void __user *arg) +{ + int mode; + + if (get_user(mode, (int __user *) arg)) + return -EFAULT; + + if (mode == SONET_FRAME_SONET) + return set_sonet(dev); + else if (mode == SONET_FRAME_SDH) + return set_sdh(dev); + + return -EINVAL; +} + static int suni_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg) { @@ -179,14 +265,16 @@ static int suni_ioctl(struct atm_dev *dev,unsigned int cmd,void __user *arg) case SONET_GETDIAG: return get_diag(dev,arg); case SONET_SETFRAMING: - if ((int)(unsigned long)arg != SONET_FRAME_SONET) return -EINVAL; - return 0; + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + return set_framing(dev, arg); case SONET_GETFRAMING: - return put_user(SONET_FRAME_SONET,(int __user *)arg) ? - -EFAULT : 0; + return get_framing(dev, arg); case SONET_GETFRSENSE: return -EINVAL; case ATM_SETLOOP: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; return set_loopback(dev,(int)(unsigned long)arg); case ATM_GETLOOP: return put_user(PRIV(dev)->loop_mode,(int __user *)arg) ? @@ -220,10 +308,6 @@ static int suni_start(struct atm_dev *dev) unsigned long flags; int first; - if (!(dev->phy_data = kmalloc(sizeof(struct suni_priv),GFP_KERNEL))) - return -ENOMEM; - - PRIV(dev)->dev = dev; spin_lock_irqsave(&sunis_lock,flags); first = !sunis; PRIV(dev)->next = sunis; @@ -284,16 +368,21 @@ int suni_init(struct atm_dev *dev) { unsigned char mri; + if (!(dev->phy_data = kmalloc(sizeof(struct suni_priv),GFP_KERNEL))) + return -ENOMEM; + PRIV(dev)->dev = dev; + mri = GET(MRI); /* reset SUNI */ + PRIV(dev)->type = (mri & SUNI_MRI_TYPE) >> SUNI_MRI_TYPE_SHIFT; PUT(mri | SUNI_MRI_RESET,MRI); PUT(mri,MRI); PUT((GET(MT) & SUNI_MT_DS27_53),MT); /* disable all tests */ - REG_CHANGE(SUNI_TPOP_APM_S,SUNI_TPOP_APM_S_SHIFT,SUNI_TPOP_S_SONET, - TPOP_APM); /* use SONET */ + set_sonet(dev); REG_CHANGE(SUNI_TACP_IUCHP_CLP,0,SUNI_TACP_IUCHP_CLP, TACP_IUCHP); /* idle cells */ PUT(SUNI_IDLE_PATTERN,TACP_IUCPOP); dev->phy = &suni_ops; + return 0; } diff --git a/drivers/atm/suni.h b/drivers/atm/suni.h index efa79bfae75b..7e3e656b3993 100644 --- a/drivers/atm/suni.h +++ b/drivers/atm/suni.h @@ -1,7 +1,8 @@ -/* drivers/atm/suni.h - PMC PM5346 SUNI (PHY) declarations */ +/* + * drivers/atm/suni.h - S/UNI PHY driver + */ /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ - #ifndef DRIVER_ATM_SUNI_H #define DRIVER_ATM_SUNI_H @@ -39,7 +40,8 @@ #define SUNI_RLOP_LFM 0x1F /* RLOP Line FEBE MSB */ #define SUNI_TLOP_CTRL 0x20 /* TLOP Control */ #define SUNI_TLOP_DIAG 0x21 /* TLOP Diagnostic */ - /* 0x22-0x2F reserved */ + /* 0x22-0x27 reserved */ +#define SUNI_SSTB_CTRL 0x28 #define SUNI_RPOP_SC 0x30 /* RPOP Status/Control */ #define SUNI_RPOP_IS 0x31 /* RPOP Interrupt Status */ /* 0x32 reserved */ @@ -52,6 +54,7 @@ #define SUNI_RPOP_PFM 0x3B /* RPOP Path FEBE MSB */ /* 0x3C reserved */ #define SUNI_RPOP_PBC 0x3D /* RPOP Path BIP-8 Configuration */ +#define SUNI_RPOP_RC 0x3D /* RPOP Ring Control (PM5355) */ /* 0x3E-0x3F reserved */ #define SUNI_TPOP_CD 0x40 /* TPOP Control/Diagnostic */ #define SUNI_TPOP_PC 0x41 /* TPOP Pointer Control */ @@ -82,7 +85,8 @@ #define SUNI_TACP_TCC 0x65 /* TACP Transmit Cell Counter */ #define SUNI_TACP_TCCM 0x66 /* TACP Transmit Cell Counter MSB */ #define SUNI_TACP_CFG 0x67 /* TACP Configuration */ - /* 0x68-0x7F reserved */ +#define SUNI_SPTB_CTRL 0x68 /* SPTB Control */ + /* 0x69-0x7F reserved */ #define SUNI_MT 0x80 /* Master Test */ /* 0x81-0xFF reserved */ @@ -94,9 +98,18 @@ #define SUNI_MRI_ID_SHIFT 0 #define SUNI_MRI_TYPE 0x70 /* R, SUNI type (lite is 011) */ #define SUNI_MRI_TYPE_SHIFT 4 +#define SUNI_MRI_TYPE_PM5346 0x3 /* S/UNI 155 LITE */ +#define SUNI_MRI_TYPE_PM5347 0x4 /* S/UNI 155 PLUS */ +#define SUNI_MRI_TYPE_PM5350 0x7 /* S/UNI 155 ULTRA */ +#define SUNI_MRI_TYPE_PM5355 0x1 /* S/UNI 622 */ #define SUNI_MRI_RESET 0x80 /* RW, reset & power down chip 0: normal operation 1: reset & low power */ + +/* MCM is reg 0x4 */ +#define SUNI_MCM_LLE 0x20 /* line loopback (PM5355) */ +#define SUNI_MCM_DLE 0x10 /* diagnostic loopback (PM5355) */ + /* MCT is reg 5 */ #define SUNI_MCT_LOOPT 0x01 /* RW, timing source, 0: from TRCLK+/- */ @@ -144,6 +157,12 @@ /* TLOP_DIAG is reg 0x21 */ #define SUNI_TLOP_DIAG_DBIP 0x01 /* insert line BIP err (continuously) */ +/* SSTB_CTRL is reg 0x28 */ +#define SUNI_SSTB_CTRL_LEN16 0x01 /* path trace message length bit */ + +/* RPOP_RC is reg 0x3D (PM5355) */ +#define SUNI_RPOP_RC_ENSS 0x40 /* enable size bit */ + /* TPOP_DIAG is reg 0x40 */ #define SUNI_TPOP_DIAG_PAIS 0x01 /* insert STS path alarm ind (cont) */ #define SUNI_TPOP_DIAG_DB3 0x02 /* insert path BIP err (continuously) */ @@ -191,6 +210,9 @@ pattern */ #define SUNI_TACP_IUCHP_GFC_SHIFT 4 +/* SPTB_CTRL is reg 0x68 */ +#define SUNI_SPTB_CTRL_LEN16 0x01 /* path trace message length */ + /* MT is reg 0x80 */ #define SUNI_MT_HIZIO 0x01 /* RW, all but data bus & MP interface tri-state */ @@ -208,6 +230,7 @@ struct suni_priv { struct k_sonet_stats sonet_stats; /* link diagnostics */ int loop_mode; /* loopback mode */ + int type; /* phy type */ struct atm_dev *dev; /* device back-pointer */ struct suni_priv *next; /* next SUNI */ }; diff --git a/include/linux/sonet.h b/include/linux/sonet.h index 753680296e17..67ad11fcf88b 100644 --- a/include/linux/sonet.h +++ b/include/linux/sonet.h @@ -34,7 +34,7 @@ struct sonet_stats { /* clear error insertion */ #define SONET_GETDIAG _IOR('a',ATMIOC_PHYTYP+4,int) /* query error insertion */ -#define SONET_SETFRAMING _IO('a',ATMIOC_PHYTYP+5) +#define SONET_SETFRAMING _IOW('a',ATMIOC_PHYTYP+5,int) /* set framing mode (SONET/SDH) */ #define SONET_GETFRAMING _IOR('a',ATMIOC_PHYTYP+6,int) /* get framing mode */ -- cgit v1.2.3 From c1da4ac752b8b0411791d26c678fcf23d2eed242 Mon Sep 17 00:00:00 2001 From: Or Gerlitz Date: Fri, 13 Jun 2008 18:12:00 -0700 Subject: net/core: add NETDEV_BONDING_FAILOVER event Add NETDEV_BONDING_FAILOVER event to be used in a successive patch by bonding to announce fail-over for the active-backup mode through the netdev events notifier chain mechanism. Such an event can be of use for the RDMA CM (communication manager) to let native RDMA ULPs (eg NFS-RDMA, iSER) always be aligned with the IP stack, in the sense that they use the same ports/links as the stack does. More usages can be done to allow monitoring tools based on netlink events being aware to bonding fail-over. Signed-off-by: Or Gerlitz Signed-off-by: Jay Vosburgh Signed-off-by: Jeff Garzik --- include/linux/netdevice.h | 1 + include/linux/notifier.h | 1 + net/core/dev.c | 6 ++++++ 3 files changed, 8 insertions(+) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index f27fd2009334..e92fc839ab1d 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1479,6 +1479,7 @@ extern void __dev_addr_unsync(struct dev_addr_list **to, int *to_count, struct extern void dev_set_promiscuity(struct net_device *dev, int inc); extern void dev_set_allmulti(struct net_device *dev, int inc); extern void netdev_state_change(struct net_device *dev); +extern void netdev_bonding_change(struct net_device *dev); extern void netdev_features_change(struct net_device *dev); /* Load a device via the kmod */ extern void dev_load(struct net *net, const char *name); diff --git a/include/linux/notifier.h b/include/linux/notifier.h index 0ff6224d172a..bd3d72ddf333 100644 --- a/include/linux/notifier.h +++ b/include/linux/notifier.h @@ -197,6 +197,7 @@ static inline int notifier_to_errno(int ret) #define NETDEV_GOING_DOWN 0x0009 #define NETDEV_CHANGENAME 0x000A #define NETDEV_FEAT_CHANGE 0x000B +#define NETDEV_BONDING_FAILOVER 0x000C #define SYS_DOWN 0x0001 /* Notify of system down */ #define SYS_RESTART SYS_DOWN diff --git a/net/core/dev.c b/net/core/dev.c index 68d8df0992ab..0e45742e7158 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -961,6 +961,12 @@ void netdev_state_change(struct net_device *dev) } } +void netdev_bonding_change(struct net_device *dev) +{ + call_netdevice_notifiers(NETDEV_BONDING_FAILOVER, dev); +} +EXPORT_SYMBOL(netdev_bonding_change); + /** * dev_load - load a network module * @net: the applicable net namespace -- cgit v1.2.3 From b8a9787eddb0e4665f31dd1d64584732b2b5d051 Mon Sep 17 00:00:00 2001 From: Jay Vosburgh Date: Fri, 13 Jun 2008 18:12:04 -0700 Subject: bonding: Allow setting max_bonds to zero Permit bonding to function rationally if max_bonds is set to zero. This will load the module, but create no master devices (which can be created via sysfs). Requires some change to bond_create_sysfs; currently, the netdev sysfs directory is determined from the first bonding device created, but this is no longer possible. Instead, an interface from net/core is created to create and destroy files in net_class. Based on a patch submitted by Phil Oester . Modified by Jay Vosburgh to fix the sysfs issue mentioned above and to update the documentation. Signed-off-by: Phil Oester Signed-off-by: Jay Vosburgh Signed-off-by: Jeff Garzik --- Documentation/networking/bonding.txt | 3 ++- drivers/net/bonding/bond_main.c | 6 +++--- drivers/net/bonding/bond_sysfs.c | 22 +++------------------- include/linux/netdevice.h | 3 +++ net/core/net-sysfs.c | 13 +++++++++++++ 5 files changed, 24 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/Documentation/networking/bonding.txt b/Documentation/networking/bonding.txt index 370b7da73ab4..7fa7fe71d7a8 100644 --- a/Documentation/networking/bonding.txt +++ b/Documentation/networking/bonding.txt @@ -376,7 +376,8 @@ max_bonds Specifies the number of bonding devices to create for this instance of the bonding driver. E.g., if max_bonds is 3, and the bonding driver is not already loaded, then bond0, bond1 - and bond2 will be created. The default value is 1. + and bond2 will be created. The default value is 1. Specifying + a value of 0 will load bonding, but will not create any devices. miimon diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 3b6d66a8ab98..d57b65dc2c72 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -4750,11 +4750,11 @@ static int bond_check_params(struct bond_params *params) } } - if (max_bonds < 1 || max_bonds > INT_MAX) { + if (max_bonds < 0 || max_bonds > INT_MAX) { printk(KERN_WARNING DRV_NAME ": Warning: max_bonds (%d) not in range %d-%d, so it " "was reset to BOND_DEFAULT_MAX_BONDS (%d)\n", - max_bonds, 1, INT_MAX, BOND_DEFAULT_MAX_BONDS); + max_bonds, 0, INT_MAX, BOND_DEFAULT_MAX_BONDS); max_bonds = BOND_DEFAULT_MAX_BONDS; } @@ -4953,7 +4953,7 @@ static int bond_check_params(struct bond_params *params) printk("\n"); - } else { + } else if (max_bonds) { /* miimon and arp_interval not set, we need one so things * work as expected, see bonding.txt for details */ diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c index dd265c69b0df..6caac0ffb2f2 100644 --- a/drivers/net/bonding/bond_sysfs.c +++ b/drivers/net/bonding/bond_sysfs.c @@ -53,7 +53,6 @@ extern struct bond_parm_tbl arp_validate_tbl[]; extern struct bond_parm_tbl fail_over_mac_tbl[]; static int expected_refcount = -1; -static struct class *netdev_class; /*--------------------------- Data Structures -----------------------------*/ /* Bonding sysfs lock. Why can't we just use the subsystem lock? @@ -1447,19 +1446,9 @@ static struct attribute_group bonding_group = { */ int bond_create_sysfs(void) { - int ret = 0; - struct bonding *firstbond; - - /* get the netdev class pointer */ - firstbond = container_of(bond_dev_list.next, struct bonding, bond_list); - if (!firstbond) - return -ENODEV; - - netdev_class = firstbond->dev->dev.class; - if (!netdev_class) - return -ENODEV; + int ret; - ret = class_create_file(netdev_class, &class_attr_bonding_masters); + ret = netdev_class_create_file(&class_attr_bonding_masters); /* * Permit multiple loads of the module by ignoring failures to * create the bonding_masters sysfs file. Bonding devices @@ -1478,10 +1467,6 @@ int bond_create_sysfs(void) printk(KERN_ERR "network device named %s already exists in sysfs", class_attr_bonding_masters.attr.name); - else { - netdev_class = NULL; - return 0; - } } return ret; @@ -1493,8 +1478,7 @@ int bond_create_sysfs(void) */ void bond_destroy_sysfs(void) { - if (netdev_class) - class_remove_file(netdev_class, &class_attr_bonding_masters); + netdev_class_remove_file(&class_attr_bonding_masters); } /* diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index e92fc839ab1d..9ccbfac3fd95 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1506,6 +1506,9 @@ extern void *dev_seq_next(struct seq_file *seq, void *v, loff_t *pos); extern void dev_seq_stop(struct seq_file *seq, void *v); #endif +extern int netdev_class_create_file(struct class_attribute *class_attr); +extern void netdev_class_remove_file(struct class_attribute *class_attr); + extern void linkwatch_run_queue(void); extern int netdev_compute_features(unsigned long all, unsigned long one); diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index dccd737ea2e3..3f7941319217 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -468,6 +468,19 @@ int netdev_register_kobject(struct net_device *net) return device_add(dev); } +int netdev_class_create_file(struct class_attribute *class_attr) +{ + return class_create_file(&net_class, class_attr); +} + +void netdev_class_remove_file(struct class_attribute *class_attr) +{ + class_remove_file(&net_class, class_attr); +} + +EXPORT_SYMBOL(netdev_class_create_file); +EXPORT_SYMBOL(netdev_class_remove_file); + void netdev_initialize_kobject(struct net_device *net) { struct device *device = &(net->dev); -- cgit v1.2.3 From 9d45abe1c2949183e5d9cb25721bf1c42c7b5e3b Mon Sep 17 00:00:00 2001 From: Wang Chen Date: Tue, 17 Jun 2008 21:12:48 -0700 Subject: netdevice: change net_device->promiscuity/allmulti to unsigned int The comments of dev_set_allmulti/promiscuity() is that "While the count in the device remains above zero...". So negative count is useless. Fix the type of the counter from "int" to "unsigned int". Signed-off-by: Wang Chen Signed-off-by: David S. Miller --- include/linux/netdevice.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index f27fd2009334..06d8ea5992df 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -595,8 +595,8 @@ struct net_device int uc_promisc; struct dev_addr_list *mc_list; /* Multicast mac addresses */ int mc_count; /* Number of installed mcasts */ - int promiscuity; - int allmulti; + unsigned int promiscuity; + unsigned int allmulti; /* Protocol specific pointers */ -- cgit v1.2.3 From dad9b335c6940de2746a9788eb456d09cf102f81 Mon Sep 17 00:00:00 2001 From: Wang Chen Date: Wed, 18 Jun 2008 01:48:28 -0700 Subject: netdevice: Fix promiscuity and allmulti overflow Max of promiscuity and allmulti plus positive @inc can cause overflow. Fox example: when allmulti=0xFFFFFFFF, any caller give dev_set_allmulti() a positive @inc will cause allmulti be off. This is not what we want, though it's rare case. The fix is that only negative @inc will cause allmulti or promiscuity be off and when any caller makes the counters touch the roof, we return error. Change of v2: Change void function dev_set_promiscuity/allmulti to return int. So callers can get the overflow error. Caller's fix will be done later. Change of v3: 1. Since we return error to caller, we don't need to print KERN_ERROR, KERN_WARNING is enough. 2. In dev_set_promiscuity(), if __dev_set_promiscuity() failed, we return at once. Signed-off-by: Wang Chen Acked-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netdevice.h | 4 ++-- net/core/dev.c | 55 ++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 47 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 4bf613cd9e2d..45dce2b58d4c 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1476,8 +1476,8 @@ extern int __dev_addr_delete(struct dev_addr_list **list, int *count, void *ad extern int __dev_addr_add(struct dev_addr_list **list, int *count, void *addr, int alen, int newonly); extern int __dev_addr_sync(struct dev_addr_list **to, int *to_count, struct dev_addr_list **from, int *from_count); extern void __dev_addr_unsync(struct dev_addr_list **to, int *to_count, struct dev_addr_list **from, int *from_count); -extern void dev_set_promiscuity(struct net_device *dev, int inc); -extern void dev_set_allmulti(struct net_device *dev, int inc); +extern int dev_set_promiscuity(struct net_device *dev, int inc); +extern int dev_set_allmulti(struct net_device *dev, int inc); extern void netdev_state_change(struct net_device *dev); extern void netdev_bonding_change(struct net_device *dev); extern void netdev_features_change(struct net_device *dev); diff --git a/net/core/dev.c b/net/core/dev.c index 0e45742e7158..a495f712d38c 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2771,16 +2771,29 @@ int netdev_set_master(struct net_device *slave, struct net_device *master) return 0; } -static void __dev_set_promiscuity(struct net_device *dev, int inc) +static int __dev_set_promiscuity(struct net_device *dev, int inc) { unsigned short old_flags = dev->flags; ASSERT_RTNL(); - if ((dev->promiscuity += inc) == 0) - dev->flags &= ~IFF_PROMISC; - else - dev->flags |= IFF_PROMISC; + dev->flags |= IFF_PROMISC; + dev->promiscuity += inc; + if (dev->promiscuity == 0) { + /* + * Avoid overflow. + * If inc causes overflow, untouch promisc and return error. + */ + if (inc < 0) + dev->flags &= ~IFF_PROMISC; + else { + dev->promiscuity -= inc; + printk(KERN_WARNING "%s: promiscuity touches roof, " + "set promiscuity failed, promiscuity feature " + "of device might be broken.\n", dev->name); + return -EOVERFLOW; + } + } if (dev->flags != old_flags) { printk(KERN_INFO "device %s %s promiscuous mode\n", dev->name, (dev->flags & IFF_PROMISC) ? "entered" : @@ -2798,6 +2811,7 @@ static void __dev_set_promiscuity(struct net_device *dev, int inc) if (dev->change_rx_flags) dev->change_rx_flags(dev, IFF_PROMISC); } + return 0; } /** @@ -2809,14 +2823,19 @@ static void __dev_set_promiscuity(struct net_device *dev, int inc) * remains above zero the interface remains promiscuous. Once it hits zero * the device reverts back to normal filtering operation. A negative inc * value is used to drop promiscuity on the device. + * Return 0 if successful or a negative errno code on error. */ -void dev_set_promiscuity(struct net_device *dev, int inc) +int dev_set_promiscuity(struct net_device *dev, int inc) { unsigned short old_flags = dev->flags; + int err; - __dev_set_promiscuity(dev, inc); + err = __dev_set_promiscuity(dev, inc); + if (!err) + return err; if (dev->flags != old_flags) dev_set_rx_mode(dev); + return err; } /** @@ -2829,22 +2848,38 @@ void dev_set_promiscuity(struct net_device *dev, int inc) * to all interfaces. Once it hits zero the device reverts back to normal * filtering operation. A negative @inc value is used to drop the counter * when releasing a resource needing all multicasts. + * Return 0 if successful or a negative errno code on error. */ -void dev_set_allmulti(struct net_device *dev, int inc) +int dev_set_allmulti(struct net_device *dev, int inc) { unsigned short old_flags = dev->flags; ASSERT_RTNL(); dev->flags |= IFF_ALLMULTI; - if ((dev->allmulti += inc) == 0) - dev->flags &= ~IFF_ALLMULTI; + dev->allmulti += inc; + if (dev->allmulti == 0) { + /* + * Avoid overflow. + * If inc causes overflow, untouch allmulti and return error. + */ + if (inc < 0) + dev->flags &= ~IFF_ALLMULTI; + else { + dev->allmulti -= inc; + printk(KERN_WARNING "%s: allmulti touches roof, " + "set allmulti failed, allmulti feature of " + "device might be broken.\n", dev->name); + return -EOVERFLOW; + } + } if (dev->flags ^ old_flags) { if (dev->change_rx_flags) dev->change_rx_flags(dev, IFF_ALLMULTI); dev_set_rx_mode(dev); } + return 0; } /* -- cgit v1.2.3 From 0187bdfb05674147774ca79a79942537f3ad54bd Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 19 Jun 2008 16:15:47 -0700 Subject: net: Disable LRO on devices that are forwarding Large Receive Offload (LRO) is only appropriate for packets that are destined for the host, and should be disabled if received packets may be forwarded. It can also confuse the GSO on output. Add dev_disable_lro() function which uses the appropriate ethtool ops to disable LRO if enabled. Add calls to dev_disable_lro() in br_add_if() and functions that enable IPv4 and IPv6 forwarding. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 + net/bridge/br_if.c | 1 + net/core/dev.c | 24 ++++++++++++++++++++++++ net/ipv4/devinet.c | 21 ++++++++++++++++----- net/ipv6/addrconf.c | 6 ++++++ 5 files changed, 48 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 45dce2b58d4c..1304ad2d7105 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -886,6 +886,7 @@ extern struct net_device *__dev_get_by_name(struct net *net, const char *name); extern int dev_alloc_name(struct net_device *dev, const char *name); extern int dev_open(struct net_device *dev); extern int dev_close(struct net_device *dev); +extern void dev_disable_lro(struct net_device *dev); extern int dev_queue_xmit(struct sk_buff *skb); extern int register_netdevice(struct net_device *dev); extern void unregister_netdevice(struct net_device *dev); diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 143c954681b8..832a561500d9 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -387,6 +387,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) goto err2; rcu_assign_pointer(dev->br_port, p); + dev_disable_lro(dev); dev_set_promiscuity(dev, 1); list_add_rcu(&p->list, &br->port_list); diff --git a/net/core/dev.c b/net/core/dev.c index a495f712d38c..f6944ecd5b2e 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -90,6 +90,7 @@ #include #include #include +#include #include #include #include @@ -1123,6 +1124,29 @@ int dev_close(struct net_device *dev) } +/** + * dev_disable_lro - disable Large Receive Offload on a device + * @dev: device + * + * Disable Large Receive Offload (LRO) on a net device. Must be + * called under RTNL. This is needed if received packets may be + * forwarded to another interface. + */ +void dev_disable_lro(struct net_device *dev) +{ + if (dev->ethtool_ops && dev->ethtool_ops->get_flags && + dev->ethtool_ops->set_flags) { + u32 flags = dev->ethtool_ops->get_flags(dev); + if (flags & ETH_FLAG_LRO) { + flags &= ~ETH_FLAG_LRO; + dev->ethtool_ops->set_flags(dev, flags); + } + } + WARN_ON(dev->features & NETIF_F_LRO); +} +EXPORT_SYMBOL(dev_disable_lro); + + static int dev_boot_phase = 1; /* diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index f8c0b0aea93a..9de2514946ca 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -168,6 +168,8 @@ static struct in_device *inetdev_init(struct net_device *dev) in_dev->dev = dev; if ((in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl)) == NULL) goto out_kfree; + if (IPV4_DEVCONF(in_dev->cnf, FORWARDING)) + dev_disable_lro(dev); /* Reference in_dev->dev */ dev_hold(dev); /* Account for reference dev->ip_ptr (below) */ @@ -1241,6 +1243,8 @@ static void inet_forward_change(struct net *net) read_lock(&dev_base_lock); for_each_netdev(net, dev) { struct in_device *in_dev; + if (on) + dev_disable_lro(dev); rcu_read_lock(); in_dev = __in_dev_get_rcu(dev); if (in_dev) @@ -1248,8 +1252,6 @@ static void inet_forward_change(struct net *net) rcu_read_unlock(); } read_unlock(&dev_base_lock); - - rt_cache_flush(0); } static int devinet_conf_proc(ctl_table *ctl, int write, @@ -1335,10 +1337,19 @@ static int devinet_sysctl_forward(ctl_table *ctl, int write, if (write && *valp != val) { struct net *net = ctl->extra2; - if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) - inet_forward_change(net); - else if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) + if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) { + rtnl_lock(); + if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) { + inet_forward_change(net); + } else if (*valp) { + struct ipv4_devconf *cnf = ctl->extra1; + struct in_device *idev = + container_of(cnf, struct in_device, cnf); + dev_disable_lro(idev->dev); + } + rtnl_unlock(); rt_cache_flush(0); + } } return ret; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 9be6be3a7ff3..84127d854cfc 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -348,6 +348,8 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev) kfree(ndev); return NULL; } + if (ndev->cnf.forwarding) + dev_disable_lro(dev); /* We refer to the device */ dev_hold(dev); @@ -442,6 +444,8 @@ static void dev_forward_change(struct inet6_dev *idev) if (!idev) return; dev = idev->dev; + if (idev->cnf.forwarding) + dev_disable_lro(dev); if (dev && (dev->flags & IFF_MULTICAST)) { if (idev->cnf.forwarding) ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters); @@ -487,12 +491,14 @@ static void addrconf_fixup_forwarding(struct ctl_table *table, int *p, int old) if (p == &net->ipv6.devconf_dflt->forwarding) return; + rtnl_lock(); if (p == &net->ipv6.devconf_all->forwarding) { __s32 newf = net->ipv6.devconf_all->forwarding; net->ipv6.devconf_dflt->forwarding = newf; addrconf_forward_change(net, newf); } else if ((!*p) ^ (!old)) dev_forward_change((struct inet6_dev *)table->extra1); + rtnl_unlock(); if (*p) rt6_purge_dflt_routers(net); -- cgit v1.2.3 From 4497b0763cb1afae463f5e144c28b5d806e28b60 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 19 Jun 2008 16:22:28 -0700 Subject: net: Discard and warn about LRO'd skbs received for forwarding Add skb_warn_if_lro() to test whether an skb was received with LRO and warn if so. Change br_forward(), ip_forward() and ip6_forward() to call it) and discard the skb if it returns true. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- include/linux/skbuff.h | 14 ++++++++++++++ net/bridge/br_forward.c | 2 +- net/core/skbuff.c | 8 ++++++++ net/ipv4/ip_forward.c | 3 +++ net/ipv6/ip6_output.c | 3 +++ 5 files changed, 29 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 299ec4b31412..2220b9e2dab0 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1702,6 +1702,20 @@ static inline int skb_is_gso_v6(const struct sk_buff *skb) return skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6; } +extern void __skb_warn_lro_forwarding(const struct sk_buff *skb); + +static inline bool skb_warn_if_lro(const struct sk_buff *skb) +{ + /* LRO sets gso_size but not gso_type, whereas if GSO is really + * wanted then gso_type will be set. */ + struct skb_shared_info *shinfo = skb_shinfo(skb); + if (shinfo->gso_size != 0 && unlikely(shinfo->gso_type == 0)) { + __skb_warn_lro_forwarding(skb); + return true; + } + return false; +} + static inline void skb_forward_csum(struct sk_buff *skb) { /* Unfortunately we don't support this one. Any brave souls? */ diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 512645727f51..bdd9ccea17ce 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -89,7 +89,7 @@ void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb) /* called with rcu_read_lock */ void br_forward(const struct net_bridge_port *to, struct sk_buff *skb) { - if (should_deliver(to, skb)) { + if (!skb_warn_if_lro(skb) && should_deliver(to, skb)) { __br_forward(to, skb); return; } diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 3e18f8525e82..2df012be973d 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -2583,6 +2583,13 @@ bool skb_partial_csum_set(struct sk_buff *skb, u16 start, u16 off) return true; } +void __skb_warn_lro_forwarding(const struct sk_buff *skb) +{ + if (net_ratelimit()) + pr_warning("%s: received packets cannot be forwarded" + " while LRO is enabled\n", skb->dev->name); +} + EXPORT_SYMBOL(___pskb_trim); EXPORT_SYMBOL(__kfree_skb); EXPORT_SYMBOL(kfree_skb); @@ -2616,6 +2623,7 @@ EXPORT_SYMBOL(skb_seq_read); EXPORT_SYMBOL(skb_abort_seq_read); EXPORT_SYMBOL(skb_find_text); EXPORT_SYMBOL(skb_append_datato_frags); +EXPORT_SYMBOL(__skb_warn_lro_forwarding); EXPORT_SYMBOL_GPL(skb_to_sgvec); EXPORT_SYMBOL_GPL(skb_cow_data); diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c index 37d36a3f33cd..da14725916d3 100644 --- a/net/ipv4/ip_forward.c +++ b/net/ipv4/ip_forward.c @@ -56,6 +56,9 @@ int ip_forward(struct sk_buff *skb) struct rtable *rt; /* Route we use */ struct ip_options * opt = &(IPCB(skb)->opt); + if (skb_warn_if_lro(skb)) + goto drop; + if (!xfrm4_policy_check(NULL, XFRM_POLICY_FWD, skb)) goto drop; diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 40a2813a63d1..fd7cd1bfe151 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -407,6 +407,9 @@ int ip6_forward(struct sk_buff *skb) if (ipv6_devconf.forwarding == 0) goto error; + if (skb_warn_if_lro(skb)) + goto drop; + if (!xfrm6_policy_check(NULL, XFRM_POLICY_FWD, skb)) { IP6_INC_STATS(ip6_dst_idev(dst), IPSTATS_MIB_INDISCARDS); goto drop; -- cgit v1.2.3 From 7c11337d9d81cde0a08a0da63cbfb20653890fa1 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Wed, 4 Jun 2008 18:50:06 -0400 Subject: nfsd: remove three unused NFS4_ACE_* defines These flag bits aren't used by either the protocol or our implementation, so I don't know why they were here. Thanks to Johann Dahm for running across these. Signed-off-by: J. Bruce Fields Cc: Johann Dahm --- include/linux/nfs4.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index 8726491de154..ea0366769484 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -65,9 +65,6 @@ #define NFS4_ACE_SUCCESSFUL_ACCESS_ACE_FLAG 0x00000010 #define NFS4_ACE_FAILED_ACCESS_ACE_FLAG 0x00000020 #define NFS4_ACE_IDENTIFIER_GROUP 0x00000040 -#define NFS4_ACE_OWNER 0x00000080 -#define NFS4_ACE_GROUP 0x00000100 -#define NFS4_ACE_EVERYONE 0x00000200 #define NFS4_ACE_READ_DATA 0x00000001 #define NFS4_ACE_LIST_DIRECTORY 0x00000001 -- cgit v1.2.3 From a5e561fee651eb03086ca21e9aa78e1fffa4581a Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Tue, 10 Jun 2008 12:59:07 +0300 Subject: nfsd: eliminate unused nfs4_callback.cb_program The cb_program member of struct nfs4_callback unused since commit ff7d9756 nfsd: use static memory for callback program and stats Signed-off-by: Benny Halevy Signed-off-by: J. Bruce Fields --- include/linux/nfsd/state.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/nfsd/state.h b/include/linux/nfsd/state.h index db348f749376..06e9686d9dea 100644 --- a/include/linux/nfsd/state.h +++ b/include/linux/nfsd/state.h @@ -98,7 +98,6 @@ struct nfs4_callback { u32 cb_ident; /* RPC client info */ atomic_t cb_set; /* successful CB_NULL call */ - struct rpc_program cb_program; struct rpc_stat cb_stat; struct rpc_clnt * cb_client; }; -- cgit v1.2.3 From 0d169ca136357d51a65d686f3c84866a8ba20ae9 Mon Sep 17 00:00:00 2001 From: Benny Halevy Date: Tue, 10 Jun 2008 13:39:43 +0300 Subject: nfsd: eliminate unused nfs4_callback.cb_stat The cb_stat member of struct nfs4_callback is unused since commit ff7d9756 nfsd: use static memory for callback program and stats Signed-off-by: Benny Halevy Signed-off-by: J. Bruce Fields --- include/linux/nfsd/state.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/nfsd/state.h b/include/linux/nfsd/state.h index 06e9686d9dea..d0fe2e378452 100644 --- a/include/linux/nfsd/state.h +++ b/include/linux/nfsd/state.h @@ -98,7 +98,6 @@ struct nfs4_callback { u32 cb_ident; /* RPC client info */ atomic_t cb_set; /* successful CB_NULL call */ - struct rpc_stat cb_stat; struct rpc_clnt * cb_client; }; -- cgit v1.2.3 From bedbdd8bada194a690d2901801bf8451965086b3 Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Tue, 10 Jun 2008 08:40:35 -0400 Subject: knfsd: Replace lock_kernel with a mutex for nfsd thread startup/shutdown locking. This removes the BKL from the RPC service creation codepath. The BKL really isn't adequate for this job since some of this info needs protection across sleeps. Also, add some comments to try and clarify how the locking should work and to make it clear that the BKL isn't necessary as long as there is adequate locking between tasks when touching the svc_serv fields. Signed-off-by: Neil Brown Signed-off-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/nfsd/nfsctl.c | 37 +++++++++++++++++++++++-------------- fs/nfsd/nfssvc.c | 45 ++++++++++++++++++++++++++++++++------------- include/linux/nfsd/nfsd.h | 1 + net/sunrpc/svc.c | 15 +++++++++------ 4 files changed, 65 insertions(+), 33 deletions(-) (limited to 'include/linux') diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 5ac00c4fee91..049d2a9c7715 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -450,22 +450,26 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size) int i; int rv; int len; - int npools = nfsd_nrpools(); + int npools; int *nthreads; + mutex_lock(&nfsd_mutex); + npools = nfsd_nrpools(); if (npools == 0) { /* * NFS is shut down. The admin can start it by * writing to the threads file but NOT the pool_threads * file, sorry. Report zero threads. */ + mutex_unlock(&nfsd_mutex); strcpy(buf, "0\n"); return strlen(buf); } nthreads = kcalloc(npools, sizeof(int), GFP_KERNEL); + rv = -ENOMEM; if (nthreads == NULL) - return -ENOMEM; + goto out_free; if (size > 0) { for (i = 0; i < npools; i++) { @@ -496,10 +500,12 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size) mesg += len; } + mutex_unlock(&nfsd_mutex); return (mesg-buf); out_free: kfree(nthreads); + mutex_unlock(&nfsd_mutex); return rv; } @@ -566,14 +572,13 @@ static ssize_t write_versions(struct file *file, char *buf, size_t size) return len; } -static ssize_t write_ports(struct file *file, char *buf, size_t size) +static ssize_t __write_ports(struct file *file, char *buf, size_t size) { if (size == 0) { int len = 0; - lock_kernel(); + if (nfsd_serv) len = svc_xprt_names(nfsd_serv, buf, 0); - unlock_kernel(); return len; } /* Either a single 'fd' number is written, in which @@ -603,9 +608,7 @@ static ssize_t write_ports(struct file *file, char *buf, size_t size) /* Decrease the count, but don't shutdown the * the service */ - lock_kernel(); nfsd_serv->sv_nrthreads--; - unlock_kernel(); } return err < 0 ? err : 0; } @@ -614,10 +617,8 @@ static ssize_t write_ports(struct file *file, char *buf, size_t size) int len = 0; if (!toclose) return -ENOMEM; - lock_kernel(); if (nfsd_serv) len = svc_sock_names(buf, nfsd_serv, toclose); - unlock_kernel(); if (len >= 0) lockd_down(); kfree(toclose); @@ -655,7 +656,6 @@ static ssize_t write_ports(struct file *file, char *buf, size_t size) if (sscanf(&buf[1], "%15s %4d", transport, &port) == 2) { if (port == 0) return -EINVAL; - lock_kernel(); if (nfsd_serv) { xprt = svc_find_xprt(nfsd_serv, transport, AF_UNSPEC, port); @@ -666,13 +666,22 @@ static ssize_t write_ports(struct file *file, char *buf, size_t size) } else err = -ENOTCONN; } - unlock_kernel(); return err < 0 ? err : 0; } } return -EINVAL; } +static ssize_t write_ports(struct file *file, char *buf, size_t size) +{ + ssize_t rv; + mutex_lock(&nfsd_mutex); + rv = __write_ports(file, buf, size); + mutex_unlock(&nfsd_mutex); + return rv; +} + + int nfsd_max_blksize; static ssize_t write_maxblksize(struct file *file, char *buf, size_t size) @@ -691,13 +700,13 @@ static ssize_t write_maxblksize(struct file *file, char *buf, size_t size) if (bsize > NFSSVC_MAXBLKSIZE) bsize = NFSSVC_MAXBLKSIZE; bsize &= ~(1024-1); - lock_kernel(); + mutex_lock(&nfsd_mutex); if (nfsd_serv && nfsd_serv->sv_nrthreads) { - unlock_kernel(); + mutex_unlock(&nfsd_mutex); return -EBUSY; } nfsd_max_blksize = bsize; - unlock_kernel(); + mutex_unlock(&nfsd_mutex); } return sprintf(buf, "%d\n", nfsd_max_blksize); } diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 941041f4b136..512bd04c6dda 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -53,11 +53,27 @@ extern struct svc_program nfsd_program; static void nfsd(struct svc_rqst *rqstp); struct timeval nfssvc_boot; - struct svc_serv *nfsd_serv; static atomic_t nfsd_busy; static unsigned long nfsd_last_call; static DEFINE_SPINLOCK(nfsd_call_lock); +/* + * nfsd_mutex protects nfsd_serv -- both the pointer itself and the members + * of the svc_serv struct. In particular, ->sv_nrthreads but also to some + * extent ->sv_temp_socks and ->sv_permsocks. It also protects nfsdstats.th_cnt + * + * If (out side the lock) nfsd_serv is non-NULL, then it must point to a + * properly initialised 'struct svc_serv' with ->sv_nrthreads > 0. That number + * of nfsd threads must exist and each must listed in ->sp_all_threads in each + * entry of ->sv_pools[]. + * + * Transitions of the thread count between zero and non-zero are of particular + * interest since the svc_serv needs to be created and initialized at that + * point, or freed. + */ +DEFINE_MUTEX(nfsd_mutex); +struct svc_serv *nfsd_serv; + #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) static struct svc_stat nfsd_acl_svcstats; static struct svc_version * nfsd_acl_version[] = { @@ -190,13 +206,14 @@ void nfsd_reset_versions(void) } } + int nfsd_create_serv(void) { int err = 0; - lock_kernel(); + + WARN_ON(!mutex_is_locked(&nfsd_mutex)); if (nfsd_serv) { svc_get(nfsd_serv); - unlock_kernel(); return 0; } if (nfsd_max_blksize == 0) { @@ -223,7 +240,7 @@ int nfsd_create_serv(void) nfsd, SIG_NOCLEAN, THIS_MODULE); if (nfsd_serv == NULL) err = -ENOMEM; - unlock_kernel(); + do_gettimeofday(&nfssvc_boot); /* record boot time */ return err; } @@ -282,6 +299,8 @@ int nfsd_set_nrthreads(int n, int *nthreads) int tot = 0; int err = 0; + WARN_ON(!mutex_is_locked(&nfsd_mutex)); + if (nfsd_serv == NULL || n <= 0) return 0; @@ -316,7 +335,6 @@ int nfsd_set_nrthreads(int n, int *nthreads) nthreads[0] = 1; /* apply the new numbers */ - lock_kernel(); svc_get(nfsd_serv); for (i = 0; i < n; i++) { err = svc_set_num_threads(nfsd_serv, &nfsd_serv->sv_pools[i], @@ -325,7 +343,6 @@ int nfsd_set_nrthreads(int n, int *nthreads) break; } svc_destroy(nfsd_serv); - unlock_kernel(); return err; } @@ -334,8 +351,8 @@ int nfsd_svc(unsigned short port, int nrservs) { int error; - - lock_kernel(); + + mutex_lock(&nfsd_mutex); dprintk("nfsd: creating service\n"); error = -EINVAL; if (nrservs <= 0) @@ -363,7 +380,7 @@ nfsd_svc(unsigned short port, int nrservs) failure: svc_destroy(nfsd_serv); /* Release server */ out: - unlock_kernel(); + mutex_unlock(&nfsd_mutex); return error; } @@ -399,7 +416,7 @@ nfsd(struct svc_rqst *rqstp) sigset_t shutdown_mask, allowed_mask; /* Lock module and set up kernel thread */ - lock_kernel(); + mutex_lock(&nfsd_mutex); daemonize("nfsd"); /* After daemonize() this kernel thread shares current->fs @@ -417,11 +434,13 @@ nfsd(struct svc_rqst *rqstp) siginitsetinv(&shutdown_mask, SHUTDOWN_SIGS); siginitsetinv(&allowed_mask, ALLOWED_SIGS); + nfsdstats.th_cnt++; rqstp->rq_task = current; - unlock_kernel(); + mutex_unlock(&nfsd_mutex); + /* * We want less throttling in balance_dirty_pages() so that nfs to @@ -477,7 +496,7 @@ nfsd(struct svc_rqst *rqstp) /* Clear signals before calling svc_exit_thread() */ flush_signals(current); - lock_kernel(); + mutex_lock(&nfsd_mutex); nfsdstats.th_cnt --; @@ -486,7 +505,7 @@ out: svc_exit_thread(rqstp); /* Release module */ - unlock_kernel(); + mutex_unlock(&nfsd_mutex); module_put_and_exit(0); } diff --git a/include/linux/nfsd/nfsd.h b/include/linux/nfsd/nfsd.h index 41d30c9c9de6..88d85b964429 100644 --- a/include/linux/nfsd/nfsd.h +++ b/include/linux/nfsd/nfsd.h @@ -54,6 +54,7 @@ typedef int (*nfsd_dirop_t)(struct inode *, struct dentry *, int, int); extern struct svc_program nfsd_program; extern struct svc_version nfsd_version2, nfsd_version3, nfsd_version4; +extern struct mutex nfsd_mutex; extern struct svc_serv *nfsd_serv; extern struct seq_operations nfs_exports_op; diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 01c7e311b904..7bffaff2a3ab 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -461,7 +461,8 @@ svc_create_pooled(struct svc_program *prog, unsigned int bufsize, EXPORT_SYMBOL(svc_create_pooled); /* - * Destroy an RPC service. Should be called with the BKL held + * Destroy an RPC service. Should be called with appropriate locking to + * protect the sv_nrthreads, sv_permsocks and sv_tempsocks. */ void svc_destroy(struct svc_serv *serv) @@ -578,9 +579,10 @@ out_enomem: EXPORT_SYMBOL(svc_prepare_thread); /* - * Create a thread in the given pool. Caller must hold BKL. - * On a NUMA or SMP machine, with a multi-pool serv, the thread - * will be restricted to run on the cpus belonging to the pool. + * Create a thread in the given pool. Caller must hold BKL or another lock to + * serialize access to the svc_serv struct. On a NUMA or SMP machine, with a + * multi-pool serv, the thread will be restricted to run on the cpus belonging + * to the pool. */ static int __svc_create_thread(svc_thread_fn func, struct svc_serv *serv, @@ -674,7 +676,7 @@ found_pool: * of threads the given number. If `pool' is non-NULL, applies * only to threads in that pool, otherwise round-robins between * all pools. Must be called with a svc_get() reference and - * the BKL held. + * the BKL or another lock to protect access to svc_serv fields. * * Destroying threads relies on the service threads filling in * rqstp->rq_task, which only the nfs ones do. Assumes the serv @@ -722,7 +724,8 @@ svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs) EXPORT_SYMBOL(svc_set_num_threads); /* - * Called from a server thread as it's exiting. Caller must hold BKL. + * Called from a server thread as it's exiting. Caller must hold the BKL or + * the "service mutex", whichever is appropriate for the service. */ void svc_exit_thread(struct svc_rqst *rqstp) -- cgit v1.2.3 From 9867d76ca16b3f455f9ca83861f4ce5c94a25928 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 10 Jun 2008 08:40:38 -0400 Subject: knfsd: convert knfsd to kthread API This patch is rather large, but I couldn't figure out a way to break it up that would remain bisectable. It does several things: - change svc_thread_fn typedef to better match what kthread_create expects - change svc_pool_map_set_cpumask to be more kthread friendly. Make it take a task arg and and get rid of the "oldmask" - have svc_set_num_threads call kthread_create directly - eliminate __svc_create_thread Signed-off-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/nfsd/nfssvc.c | 45 ++++++++++++-------- include/linux/sunrpc/svc.h | 2 +- net/sunrpc/svc.c | 100 +++++++++++++++------------------------------ 3 files changed, 64 insertions(+), 83 deletions(-) (limited to 'include/linux') diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 6339cb70a08d..9e2156813710 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -46,7 +47,7 @@ #define SHUTDOWN_SIGS (sigmask(SIGKILL) | sigmask(SIGHUP) | sigmask(SIGINT) | sigmask(SIGQUIT)) extern struct svc_program nfsd_program; -static void nfsd(struct svc_rqst *rqstp); +static int nfsd(void *vrqstp); struct timeval nfssvc_boot; static atomic_t nfsd_busy; static unsigned long nfsd_last_call; @@ -407,18 +408,19 @@ update_thread_usage(int busy_threads) /* * This is the NFS server kernel thread */ -static void -nfsd(struct svc_rqst *rqstp) +static int +nfsd(void *vrqstp) { + struct svc_rqst *rqstp = (struct svc_rqst *) vrqstp; struct fs_struct *fsp; - int err; sigset_t shutdown_mask, allowed_mask; + int err, preverr = 0; + unsigned int signo; /* Lock module and set up kernel thread */ mutex_lock(&nfsd_mutex); - daemonize("nfsd"); - /* After daemonize() this kernel thread shares current->fs + /* At this point, the thread shares current->fs * with the init process. We need to create files with a * umask of 0 instead of init's umask. */ fsp = copy_fs_struct(current->fs); @@ -433,14 +435,18 @@ nfsd(struct svc_rqst *rqstp) siginitsetinv(&shutdown_mask, SHUTDOWN_SIGS); siginitsetinv(&allowed_mask, ALLOWED_SIGS); + /* + * thread is spawned with all signals set to SIG_IGN, re-enable + * the ones that matter + */ + for (signo = 1; signo <= _NSIG; signo++) { + if (!sigismember(&shutdown_mask, signo)) + allow_signal(signo); + } nfsdstats.th_cnt++; - - rqstp->rq_task = current; - mutex_unlock(&nfsd_mutex); - /* * We want less throttling in balance_dirty_pages() so that nfs to * localhost doesn't cause nfsd to lock up due to all the client's @@ -462,15 +468,25 @@ nfsd(struct svc_rqst *rqstp) */ while ((err = svc_recv(rqstp, 60*60*HZ)) == -EAGAIN) ; - if (err < 0) + if (err == -EINTR) break; + else if (err < 0) { + if (err != preverr) { + printk(KERN_WARNING "%s: unexpected error " + "from svc_recv (%d)\n", __func__, -err); + preverr = err; + } + schedule_timeout_uninterruptible(HZ); + continue; + } + update_thread_usage(atomic_read(&nfsd_busy)); atomic_inc(&nfsd_busy); /* Lock the export hash tables for reading. */ exp_readlock(); - /* Process request with signals blocked. */ + /* Process request with signals blocked. */ sigprocmask(SIG_SETMASK, &allowed_mask, NULL); svc_process(rqstp); @@ -481,14 +497,10 @@ nfsd(struct svc_rqst *rqstp) atomic_dec(&nfsd_busy); } - if (err != -EINTR) - printk(KERN_WARNING "nfsd: terminating on error %d\n", -err); - /* Clear signals before calling svc_exit_thread() */ flush_signals(current); mutex_lock(&nfsd_mutex); - nfsdstats.th_cnt --; out: @@ -498,6 +510,7 @@ out: /* Release module */ mutex_unlock(&nfsd_mutex); module_put_and_exit(0); + return 0; } static __be32 map_new_errors(u32 vers, __be32 nfserr) diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 4b54c5fdcfd9..011d6d8100d8 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -22,7 +22,7 @@ /* * This is the RPC server thread function prototype */ -typedef void (*svc_thread_fn)(struct svc_rqst *); +typedef int (*svc_thread_fn)(void *); /* * diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 7bffaff2a3ab..03a9f1a9e75c 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -291,15 +292,14 @@ svc_pool_map_put(void) /* - * Set the current thread's cpus_allowed mask so that it + * Set the given thread's cpus_allowed mask so that it * will only run on cpus in the given pool. - * - * Returns 1 and fills in oldmask iff a cpumask was applied. */ -static inline int -svc_pool_map_set_cpumask(unsigned int pidx, cpumask_t *oldmask) +static inline void +svc_pool_map_set_cpumask(struct task_struct *task, unsigned int pidx) { struct svc_pool_map *m = &svc_pool_map; + unsigned int node = m->pool_to[pidx]; /* * The caller checks for sv_nrpools > 1, which @@ -307,26 +307,17 @@ svc_pool_map_set_cpumask(unsigned int pidx, cpumask_t *oldmask) */ BUG_ON(m->count == 0); - switch (m->mode) - { - default: - return 0; + switch (m->mode) { case SVC_POOL_PERCPU: { - unsigned int cpu = m->pool_to[pidx]; - - *oldmask = current->cpus_allowed; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); - return 1; + set_cpus_allowed_ptr(task, &cpumask_of_cpu(node)); + break; } case SVC_POOL_PERNODE: { - unsigned int node = m->pool_to[pidx]; node_to_cpumask_ptr(nodecpumask, node); - - *oldmask = current->cpus_allowed; - set_cpus_allowed_ptr(current, nodecpumask); - return 1; + set_cpus_allowed_ptr(task, nodecpumask); + break; } } } @@ -578,47 +569,6 @@ out_enomem: } EXPORT_SYMBOL(svc_prepare_thread); -/* - * Create a thread in the given pool. Caller must hold BKL or another lock to - * serialize access to the svc_serv struct. On a NUMA or SMP machine, with a - * multi-pool serv, the thread will be restricted to run on the cpus belonging - * to the pool. - */ -static int -__svc_create_thread(svc_thread_fn func, struct svc_serv *serv, - struct svc_pool *pool) -{ - struct svc_rqst *rqstp; - int error = -ENOMEM; - int have_oldmask = 0; - cpumask_t uninitialized_var(oldmask); - - rqstp = svc_prepare_thread(serv, pool); - if (IS_ERR(rqstp)) { - error = PTR_ERR(rqstp); - goto out; - } - - if (serv->sv_nrpools > 1) - have_oldmask = svc_pool_map_set_cpumask(pool->sp_id, &oldmask); - - error = kernel_thread((int (*)(void *)) func, rqstp, 0); - - if (have_oldmask) - set_cpus_allowed(current, oldmask); - - if (error < 0) - goto out_thread; - svc_sock_update_bufs(serv); - error = 0; -out: - return error; - -out_thread: - svc_exit_thread(rqstp); - goto out; -} - /* * Choose a pool in which to create a new thread, for svc_set_num_threads */ @@ -688,7 +638,9 @@ found_pool: int svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs) { - struct task_struct *victim; + struct svc_rqst *rqstp; + struct task_struct *task; + struct svc_pool *chosen_pool; int error = 0; unsigned int state = serv->sv_nrthreads-1; @@ -704,18 +656,34 @@ svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs) /* create new threads */ while (nrservs > 0) { nrservs--; + chosen_pool = choose_pool(serv, pool, &state); + + rqstp = svc_prepare_thread(serv, chosen_pool); + if (IS_ERR(rqstp)) { + error = PTR_ERR(rqstp); + break; + } + __module_get(serv->sv_module); - error = __svc_create_thread(serv->sv_function, serv, - choose_pool(serv, pool, &state)); - if (error < 0) { + task = kthread_create(serv->sv_function, rqstp, serv->sv_name); + if (IS_ERR(task)) { + error = PTR_ERR(task); module_put(serv->sv_module); + svc_exit_thread(rqstp); break; } + + rqstp->rq_task = task; + if (serv->sv_nrpools > 1) + svc_pool_map_set_cpumask(task, chosen_pool->sp_id); + + svc_sock_update_bufs(serv); + wake_up_process(task); } /* destroy old threads */ while (nrservs < 0 && - (victim = choose_victim(serv, pool, &state)) != NULL) { - send_sig(serv->sv_kill_signal, victim, 1); + (task = choose_victim(serv, pool, &state)) != NULL) { + send_sig(serv->sv_kill_signal, task, 1); nrservs++; } -- cgit v1.2.3 From a75c5d01e4235a7dd785548ac756f248b1b40107 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 10 Jun 2008 08:40:39 -0400 Subject: sunrpc: remove sv_kill_signal field from svc_serv struct Since we no longer make any distinction between shutdown signals with nfsd, then it becomes easier to just standardize on a particular signal to use to bring it down (SIGINT, in this case). Signed-off-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/nfsd/nfssvc.c | 3 +-- include/linux/sunrpc/svc.h | 5 ++--- net/sunrpc/svc.c | 5 ++--- 3 files changed, 5 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 9e2156813710..26c81149d49a 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -236,8 +236,7 @@ int nfsd_create_serv(void) atomic_set(&nfsd_busy, 0); nfsd_serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize, - nfsd_last_thread, nfsd, SIGINT, - THIS_MODULE); + nfsd_last_thread, nfsd, THIS_MODULE); if (nfsd_serv == NULL) err = -ENOMEM; diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 011d6d8100d8..dc69068d94c7 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -80,7 +80,6 @@ struct svc_serv { struct module * sv_module; /* optional module to count when * adding threads */ svc_thread_fn sv_function; /* main function for threads */ - int sv_kill_signal; /* signal to kill threads */ }; /* @@ -388,8 +387,8 @@ struct svc_rqst *svc_prepare_thread(struct svc_serv *serv, struct svc_pool *pool); void svc_exit_thread(struct svc_rqst *); struct svc_serv * svc_create_pooled(struct svc_program *, unsigned int, - void (*shutdown)(struct svc_serv*), - svc_thread_fn, int sig, struct module *); + void (*shutdown)(struct svc_serv*), svc_thread_fn, + struct module *); int svc_set_num_threads(struct svc_serv *, struct svc_pool *, int); void svc_destroy(struct svc_serv *); int svc_process(struct svc_rqst *); diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 03a9f1a9e75c..5a32cb7c4bb4 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -434,7 +434,7 @@ EXPORT_SYMBOL(svc_create); struct svc_serv * svc_create_pooled(struct svc_program *prog, unsigned int bufsize, void (*shutdown)(struct svc_serv *serv), - svc_thread_fn func, int sig, struct module *mod) + svc_thread_fn func, struct module *mod) { struct svc_serv *serv; unsigned int npools = svc_pool_map_get(); @@ -443,7 +443,6 @@ svc_create_pooled(struct svc_program *prog, unsigned int bufsize, if (serv != NULL) { serv->sv_function = func; - serv->sv_kill_signal = sig; serv->sv_module = mod; } @@ -683,7 +682,7 @@ svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs) /* destroy old threads */ while (nrservs < 0 && (task = choose_victim(serv, pool, &state)) != NULL) { - send_sig(serv->sv_kill_signal, task, 1); + send_sig(SIGINT, task, 1); nrservs++; } -- cgit v1.2.3 From 8837abcab3d16608bd2c7fac051a839d48f2f30c Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Mon, 16 Jun 2008 13:20:29 +0200 Subject: nfsd: rename MAY_ flags Rename nfsd_permission() specific MAY_* flags to NFSD_MAY_* to make it clear, that these are not used outside nfsd, and to avoid name and number space conflicts with the VFS. [comment from hch: rename MAY_READ, MAY_WRITE and MAY_EXEC as well] Signed-off-by: Miklos Szeredi Signed-off-by: J. Bruce Fields --- fs/nfsd/lockd.c | 2 +- fs/nfsd/nfs2acl.c | 7 +-- fs/nfsd/nfs3acl.c | 5 +- fs/nfsd/nfs3proc.c | 8 ++-- fs/nfsd/nfs4proc.c | 23 ++++++---- fs/nfsd/nfs4state.c | 6 +-- fs/nfsd/nfsfh.c | 2 +- fs/nfsd/nfsproc.c | 8 ++-- fs/nfsd/vfs.c | 115 ++++++++++++++++++++++++---------------------- include/linux/nfsd/nfsd.h | 26 +++++------ 10 files changed, 105 insertions(+), 97 deletions(-) (limited to 'include/linux') diff --git a/fs/nfsd/lockd.c b/fs/nfsd/lockd.c index 9e4a568a5013..6b6225ac4926 100644 --- a/fs/nfsd/lockd.c +++ b/fs/nfsd/lockd.c @@ -35,7 +35,7 @@ nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp) fh.fh_export = NULL; exp_readlock(); - nfserr = nfsd_open(rqstp, &fh, S_IFREG, MAY_LOCK, filp); + nfserr = nfsd_open(rqstp, &fh, S_IFREG, NFSD_MAY_LOCK, filp); fh_put(&fh); rqstp->rq_client = NULL; exp_readunlock(); diff --git a/fs/nfsd/nfs2acl.c b/fs/nfsd/nfs2acl.c index 1c3b7654e966..4e3219e84116 100644 --- a/fs/nfsd/nfs2acl.c +++ b/fs/nfsd/nfs2acl.c @@ -40,7 +40,8 @@ static __be32 nfsacld_proc_getacl(struct svc_rqst * rqstp, dprintk("nfsd: GETACL(2acl) %s\n", SVCFH_fmt(&argp->fh)); fh = fh_copy(&resp->fh, &argp->fh); - if ((nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP))) + nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP); + if (nfserr) RETURN_STATUS(nfserr); if (argp->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT)) @@ -107,7 +108,7 @@ static __be32 nfsacld_proc_setacl(struct svc_rqst * rqstp, dprintk("nfsd: SETACL(2acl) %s\n", SVCFH_fmt(&argp->fh)); fh = fh_copy(&resp->fh, &argp->fh); - nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_SATTR); + nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_SATTR); if (!nfserr) { nfserr = nfserrno( nfsd_set_posix_acl( @@ -134,7 +135,7 @@ static __be32 nfsacld_proc_getattr(struct svc_rqst * rqstp, dprintk("nfsd: GETATTR %s\n", SVCFH_fmt(&argp->fh)); fh_copy(&resp->fh, &argp->fh); - return fh_verify(rqstp, &resp->fh, 0, MAY_NOP); + return fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP); } /* diff --git a/fs/nfsd/nfs3acl.c b/fs/nfsd/nfs3acl.c index b647f2f872dc..9981dbb377a3 100644 --- a/fs/nfsd/nfs3acl.c +++ b/fs/nfsd/nfs3acl.c @@ -36,7 +36,8 @@ static __be32 nfsd3_proc_getacl(struct svc_rqst * rqstp, __be32 nfserr = 0; fh = fh_copy(&resp->fh, &argp->fh); - if ((nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP))) + nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP); + if (nfserr) RETURN_STATUS(nfserr); if (argp->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT)) @@ -101,7 +102,7 @@ static __be32 nfsd3_proc_setacl(struct svc_rqst * rqstp, __be32 nfserr = 0; fh = fh_copy(&resp->fh, &argp->fh); - nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_SATTR); + nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_SATTR); if (!nfserr) { nfserr = nfserrno( nfsd_set_posix_acl( diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c index c721a1e6e9dd..4d617ea28cfc 100644 --- a/fs/nfsd/nfs3proc.c +++ b/fs/nfsd/nfs3proc.c @@ -63,7 +63,7 @@ nfsd3_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle *argp, SVCFH_fmt(&argp->fh)); fh_copy(&resp->fh, &argp->fh); - nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP); + nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP); if (nfserr) RETURN_STATUS(nfserr); @@ -242,7 +242,7 @@ nfsd3_proc_create(struct svc_rqst *rqstp, struct nfsd3_createargs *argp, attr = &argp->attrs; /* Get the directory inode */ - nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, MAY_CREATE); + nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, NFSD_MAY_CREATE); if (nfserr) RETURN_STATUS(nfserr); @@ -558,7 +558,7 @@ nfsd3_proc_fsinfo(struct svc_rqst * rqstp, struct nfsd_fhandle *argp, resp->f_maxfilesize = ~(u32) 0; resp->f_properties = NFS3_FSF_DEFAULT; - nfserr = fh_verify(rqstp, &argp->fh, 0, MAY_NOP); + nfserr = fh_verify(rqstp, &argp->fh, 0, NFSD_MAY_NOP); /* Check special features of the file system. May request * different read/write sizes for file systems known to have @@ -597,7 +597,7 @@ nfsd3_proc_pathconf(struct svc_rqst * rqstp, struct nfsd_fhandle *argp, resp->p_case_insensitive = 0; resp->p_case_preserving = 1; - nfserr = fh_verify(rqstp, &argp->fh, 0, MAY_NOP); + nfserr = fh_verify(rqstp, &argp->fh, 0, NFSD_MAY_NOP); if (nfserr == 0) { struct super_block *sb = argp->fh.fh_dentry->d_inode->i_sb; diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 313484380a9b..5c3683cfd59e 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -71,11 +71,11 @@ do_open_permission(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfs return nfserr_inval; if (open->op_share_access & NFS4_SHARE_ACCESS_READ) - accmode |= MAY_READ; + accmode |= NFSD_MAY_READ; if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) - accmode |= (MAY_WRITE | MAY_TRUNC); + accmode |= (NFSD_MAY_WRITE | NFSD_MAY_TRUNC); if (open->op_share_deny & NFS4_SHARE_DENY_WRITE) - accmode |= MAY_WRITE; + accmode |= NFSD_MAY_WRITE; status = fh_verify(rqstp, current_fh, S_IFREG, accmode); @@ -126,7 +126,8 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o &resfh.fh_handle.fh_base, resfh.fh_handle.fh_size); if (!created) - status = do_open_permission(rqstp, current_fh, open, MAY_NOP); + status = do_open_permission(rqstp, current_fh, open, + NFSD_MAY_NOP); out: fh_put(&resfh); @@ -157,7 +158,8 @@ do_open_fhandle(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_ open->op_truncate = (open->op_iattr.ia_valid & ATTR_SIZE) && (open->op_iattr.ia_size == 0); - status = do_open_permission(rqstp, current_fh, open, MAY_OWNER_OVERRIDE); + status = do_open_permission(rqstp, current_fh, open, + NFSD_MAY_OWNER_OVERRIDE); return status; } @@ -186,7 +188,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, cstate->current_fh.fh_handle.fh_size = rp->rp_openfh_len; memcpy(&cstate->current_fh.fh_handle.fh_base, rp->rp_openfh, rp->rp_openfh_len); - status = fh_verify(rqstp, &cstate->current_fh, 0, MAY_NOP); + status = fh_verify(rqstp, &cstate->current_fh, 0, NFSD_MAY_NOP); if (status) dprintk("nfsd4_open: replay failed" " restoring previous filehandle\n"); @@ -285,7 +287,7 @@ nfsd4_putfh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, cstate->current_fh.fh_handle.fh_size = putfh->pf_fhlen; memcpy(&cstate->current_fh.fh_handle.fh_base, putfh->pf_fhval, putfh->pf_fhlen); - return fh_verify(rqstp, &cstate->current_fh, 0, MAY_NOP); + return fh_verify(rqstp, &cstate->current_fh, 0, NFSD_MAY_NOP); } static __be32 @@ -363,7 +365,8 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, fh_init(&resfh, NFS4_FHSIZE); - status = fh_verify(rqstp, &cstate->current_fh, S_IFDIR, MAY_CREATE); + status = fh_verify(rqstp, &cstate->current_fh, S_IFDIR, + NFSD_MAY_CREATE); if (status == nfserr_symlink) status = nfserr_notdir; if (status) @@ -445,7 +448,7 @@ nfsd4_getattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, { __be32 status; - status = fh_verify(rqstp, &cstate->current_fh, 0, MAY_NOP); + status = fh_verify(rqstp, &cstate->current_fh, 0, NFSD_MAY_NOP); if (status) return status; @@ -730,7 +733,7 @@ _nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, int count; __be32 status; - status = fh_verify(rqstp, &cstate->current_fh, 0, MAY_NOP); + status = fh_verify(rqstp, &cstate->current_fh, 0, NFSD_MAY_NOP); if (status) return status; diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index bf11d6879ab4..eca8aaa450f1 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1722,9 +1722,9 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf /* Stateid was not found, this is a new OPEN */ int flags = 0; if (open->op_share_access & NFS4_SHARE_ACCESS_READ) - flags |= MAY_READ; + flags |= NFSD_MAY_READ; if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) - flags |= MAY_WRITE; + flags |= NFSD_MAY_WRITE; status = nfs4_new_open(rqstp, &stp, dp, current_fh, flags); if (status) goto out; @@ -2610,7 +2610,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, return nfserr_inval; if ((status = fh_verify(rqstp, &cstate->current_fh, - S_IFREG, MAY_LOCK))) { + S_IFREG, NFSD_MAY_LOCK))) { dprintk("NFSD: nfsd4_lock: permission denied!\n"); return status; } diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 100ae5641162..c7b0fdaeac96 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -279,7 +279,7 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) if (error) goto out; - if (!(access & MAY_LOCK)) { + if (!(access & NFSD_MAY_LOCK)) { /* * pseudoflavor restrictions are not enforced on NLM, * which clients virtually always use auth_sys for, diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c index b5a20c486712..0766f95d236a 100644 --- a/fs/nfsd/nfsproc.c +++ b/fs/nfsd/nfsproc.c @@ -65,7 +65,7 @@ nfsd_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle *argp, dprintk("nfsd: GETATTR %s\n", SVCFH_fmt(&argp->fh)); fh_copy(&resp->fh, &argp->fh); - nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP); + nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP); return nfsd_return_attrs(nfserr, resp); } @@ -215,11 +215,11 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp, SVCFH_fmt(dirfhp), argp->len, argp->name); /* First verify the parent file handle */ - nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, MAY_EXEC); + nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, NFSD_MAY_EXEC); if (nfserr) goto done; /* must fh_put dirfhp even on error */ - /* Check for MAY_WRITE in nfsd_create if necessary */ + /* Check for NFSD_MAY_WRITE in nfsd_create if necessary */ nfserr = nfserr_acces; if (!argp->len) @@ -281,7 +281,7 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp, nfserr = nfsd_permission(rqstp, newfhp->fh_export, newfhp->fh_dentry, - MAY_WRITE|MAY_LOCAL_ACCESS); + NFSD_MAY_WRITE|NFSD_MAY_LOCAL_ACCESS); if (nfserr && nfserr != nfserr_rofs) goto out_unlock; } diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index a3a291f771f4..5e05ddda4560 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -144,7 +144,7 @@ nfsd_lookup_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp, dprintk("nfsd: nfsd_lookup(fh %s, %.*s)\n", SVCFH_fmt(fhp), len,name); /* Obtain dentry and export. */ - err = fh_verify(rqstp, fhp, S_IFDIR, MAY_EXEC); + err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_EXEC); if (err) return err; @@ -262,14 +262,14 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, { struct dentry *dentry; struct inode *inode; - int accmode = MAY_SATTR; + int accmode = NFSD_MAY_SATTR; int ftype = 0; __be32 err; int host_err; int size_change = 0; if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE)) - accmode |= MAY_WRITE|MAY_OWNER_OVERRIDE; + accmode |= NFSD_MAY_WRITE|NFSD_MAY_OWNER_OVERRIDE; if (iap->ia_valid & ATTR_SIZE) ftype = S_IFREG; @@ -331,7 +331,8 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, */ if (iap->ia_valid & ATTR_SIZE) { if (iap->ia_size < inode->i_size) { - err = nfsd_permission(rqstp, fhp->fh_export, dentry, MAY_TRUNC|MAY_OWNER_OVERRIDE); + err = nfsd_permission(rqstp, fhp->fh_export, dentry, + NFSD_MAY_TRUNC|NFSD_MAY_OWNER_OVERRIDE); if (err) goto out; } @@ -462,7 +463,7 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned int flags = 0; /* Get inode */ - error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, MAY_SATTR); + error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, NFSD_MAY_SATTR); if (error) return error; @@ -563,20 +564,20 @@ struct accessmap { int how; }; static struct accessmap nfs3_regaccess[] = { - { NFS3_ACCESS_READ, MAY_READ }, - { NFS3_ACCESS_EXECUTE, MAY_EXEC }, - { NFS3_ACCESS_MODIFY, MAY_WRITE|MAY_TRUNC }, - { NFS3_ACCESS_EXTEND, MAY_WRITE }, + { NFS3_ACCESS_READ, NFSD_MAY_READ }, + { NFS3_ACCESS_EXECUTE, NFSD_MAY_EXEC }, + { NFS3_ACCESS_MODIFY, NFSD_MAY_WRITE|NFSD_MAY_TRUNC }, + { NFS3_ACCESS_EXTEND, NFSD_MAY_WRITE }, { 0, 0 } }; static struct accessmap nfs3_diraccess[] = { - { NFS3_ACCESS_READ, MAY_READ }, - { NFS3_ACCESS_LOOKUP, MAY_EXEC }, - { NFS3_ACCESS_MODIFY, MAY_EXEC|MAY_WRITE|MAY_TRUNC }, - { NFS3_ACCESS_EXTEND, MAY_EXEC|MAY_WRITE }, - { NFS3_ACCESS_DELETE, MAY_REMOVE }, + { NFS3_ACCESS_READ, NFSD_MAY_READ }, + { NFS3_ACCESS_LOOKUP, NFSD_MAY_EXEC }, + { NFS3_ACCESS_MODIFY, NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC}, + { NFS3_ACCESS_EXTEND, NFSD_MAY_EXEC|NFSD_MAY_WRITE }, + { NFS3_ACCESS_DELETE, NFSD_MAY_REMOVE }, { 0, 0 } }; @@ -589,10 +590,10 @@ static struct accessmap nfs3_anyaccess[] = { * mainly at mode bits, and we make sure to ignore read-only * filesystem checks */ - { NFS3_ACCESS_READ, MAY_READ }, - { NFS3_ACCESS_EXECUTE, MAY_EXEC }, - { NFS3_ACCESS_MODIFY, MAY_WRITE|MAY_LOCAL_ACCESS }, - { NFS3_ACCESS_EXTEND, MAY_WRITE|MAY_LOCAL_ACCESS }, + { NFS3_ACCESS_READ, NFSD_MAY_READ }, + { NFS3_ACCESS_EXECUTE, NFSD_MAY_EXEC }, + { NFS3_ACCESS_MODIFY, NFSD_MAY_WRITE|NFSD_MAY_LOCAL_ACCESS }, + { NFS3_ACCESS_EXTEND, NFSD_MAY_WRITE|NFSD_MAY_LOCAL_ACCESS }, { 0, 0 } }; @@ -606,7 +607,7 @@ nfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp, u32 *access, u32 *suppor u32 query, result = 0, sresult = 0; __be32 error; - error = fh_verify(rqstp, fhp, 0, MAY_NOP); + error = fh_verify(rqstp, fhp, 0, NFSD_MAY_NOP); if (error) goto out; @@ -678,7 +679,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, * and (hopefully) checked permission - so allow OWNER_OVERRIDE * in case a chmod has now revoked permission. */ - err = fh_verify(rqstp, fhp, type, access | MAY_OWNER_OVERRIDE); + err = fh_verify(rqstp, fhp, type, access | NFSD_MAY_OWNER_OVERRIDE); if (err) goto out; @@ -689,7 +690,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, * or any access when mandatory locking enabled */ err = nfserr_perm; - if (IS_APPEND(inode) && (access & MAY_WRITE)) + if (IS_APPEND(inode) && (access & NFSD_MAY_WRITE)) goto out; /* * We must ignore files (but only files) which might have mandatory @@ -706,14 +707,14 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, * Check to see if there are any leases on this file. * This may block while leases are broken. */ - host_err = break_lease(inode, O_NONBLOCK | ((access & MAY_WRITE) ? FMODE_WRITE : 0)); + host_err = break_lease(inode, O_NONBLOCK | ((access & NFSD_MAY_WRITE) ? FMODE_WRITE : 0)); if (host_err == -EWOULDBLOCK) host_err = -ETIMEDOUT; if (host_err) /* NOMEM or WOULDBLOCK */ goto out_nfserr; - if (access & MAY_WRITE) { - if (access & MAY_READ) + if (access & NFSD_MAY_WRITE) { + if (access & NFSD_MAY_READ) flags = O_RDWR|O_LARGEFILE; else flags = O_WRONLY|O_LARGEFILE; @@ -1069,12 +1070,12 @@ nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, if (file) { err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry, - MAY_READ|MAY_OWNER_OVERRIDE); + NFSD_MAY_READ|NFSD_MAY_OWNER_OVERRIDE); if (err) goto out; err = nfsd_vfs_read(rqstp, fhp, file, offset, vec, vlen, count); } else { - err = nfsd_open(rqstp, fhp, S_IFREG, MAY_READ, &file); + err = nfsd_open(rqstp, fhp, S_IFREG, NFSD_MAY_READ, &file); if (err) goto out; err = nfsd_vfs_read(rqstp, fhp, file, offset, vec, vlen, count); @@ -1098,13 +1099,13 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, if (file) { err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry, - MAY_WRITE|MAY_OWNER_OVERRIDE); + NFSD_MAY_WRITE|NFSD_MAY_OWNER_OVERRIDE); if (err) goto out; err = nfsd_vfs_write(rqstp, fhp, file, offset, vec, vlen, cnt, stablep); } else { - err = nfsd_open(rqstp, fhp, S_IFREG, MAY_WRITE, &file); + err = nfsd_open(rqstp, fhp, S_IFREG, NFSD_MAY_WRITE, &file); if (err) goto out; @@ -1136,7 +1137,8 @@ nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp, if ((u64)count > ~(u64)offset) return nfserr_inval; - if ((err = nfsd_open(rqstp, fhp, S_IFREG, MAY_WRITE, &file)) != 0) + err = nfsd_open(rqstp, fhp, S_IFREG, NFSD_MAY_WRITE, &file); + if (err) return err; if (EX_ISSYNC(fhp->fh_export)) { if (file->f_op && file->f_op->fsync) { @@ -1197,7 +1199,7 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, if (isdotent(fname, flen)) goto out; - err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE); + err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE); if (err) goto out; @@ -1334,7 +1336,7 @@ nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp, goto out; if (!(iap->ia_valid & ATTR_MODE)) iap->ia_mode = 0; - err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE); + err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE); if (err) goto out; @@ -1471,7 +1473,7 @@ nfsd_readlink(struct svc_rqst *rqstp, struct svc_fh *fhp, char *buf, int *lenp) __be32 err; int host_err; - err = fh_verify(rqstp, fhp, S_IFLNK, MAY_NOP); + err = fh_verify(rqstp, fhp, S_IFLNK, NFSD_MAY_NOP); if (err) goto out; @@ -1526,7 +1528,7 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, if (isdotent(fname, flen)) goto out; - err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE); + err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE); if (err) goto out; fh_lock(fhp); @@ -1591,10 +1593,10 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, __be32 err; int host_err; - err = fh_verify(rqstp, ffhp, S_IFDIR, MAY_CREATE); + err = fh_verify(rqstp, ffhp, S_IFDIR, NFSD_MAY_CREATE); if (err) goto out; - err = fh_verify(rqstp, tfhp, -S_IFDIR, MAY_NOP); + err = fh_verify(rqstp, tfhp, -S_IFDIR, NFSD_MAY_NOP); if (err) goto out; @@ -1661,10 +1663,10 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, __be32 err; int host_err; - err = fh_verify(rqstp, ffhp, S_IFDIR, MAY_REMOVE); + err = fh_verify(rqstp, ffhp, S_IFDIR, NFSD_MAY_REMOVE); if (err) goto out; - err = fh_verify(rqstp, tfhp, S_IFDIR, MAY_CREATE); + err = fh_verify(rqstp, tfhp, S_IFDIR, NFSD_MAY_CREATE); if (err) goto out; @@ -1768,7 +1770,7 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, err = nfserr_acces; if (!flen || isdotent(fname, flen)) goto out; - err = fh_verify(rqstp, fhp, S_IFDIR, MAY_REMOVE); + err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_REMOVE); if (err) goto out; @@ -1834,7 +1836,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t *offsetp, struct file *file; loff_t offset = *offsetp; - err = nfsd_open(rqstp, fhp, S_IFDIR, MAY_READ, &file); + err = nfsd_open(rqstp, fhp, S_IFDIR, NFSD_MAY_READ, &file); if (err) goto out; @@ -1875,7 +1877,7 @@ out: __be32 nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct kstatfs *stat) { - __be32 err = fh_verify(rqstp, fhp, 0, MAY_NOP); + __be32 err = fh_verify(rqstp, fhp, 0, NFSD_MAY_NOP); if (!err && vfs_statfs(fhp->fh_dentry,stat)) err = nfserr_io; return err; @@ -1896,18 +1898,18 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp, struct inode *inode = dentry->d_inode; int err; - if (acc == MAY_NOP) + if (acc == NFSD_MAY_NOP) return 0; #if 0 dprintk("nfsd: permission 0x%x%s%s%s%s%s%s%s mode 0%o%s%s%s\n", acc, - (acc & MAY_READ)? " read" : "", - (acc & MAY_WRITE)? " write" : "", - (acc & MAY_EXEC)? " exec" : "", - (acc & MAY_SATTR)? " sattr" : "", - (acc & MAY_TRUNC)? " trunc" : "", - (acc & MAY_LOCK)? " lock" : "", - (acc & MAY_OWNER_OVERRIDE)? " owneroverride" : "", + (acc & NFSD_MAY_READ)? " read" : "", + (acc & NFSD_MAY_WRITE)? " write" : "", + (acc & NFSD_MAY_EXEC)? " exec" : "", + (acc & NFSD_MAY_SATTR)? " sattr" : "", + (acc & NFSD_MAY_TRUNC)? " trunc" : "", + (acc & NFSD_MAY_LOCK)? " lock" : "", + (acc & NFSD_MAY_OWNER_OVERRIDE)? " owneroverride" : "", inode->i_mode, IS_IMMUTABLE(inode)? " immut" : "", IS_APPEND(inode)? " append" : "", @@ -1920,18 +1922,18 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp, * system. But if it is IRIX doing check on write-access for a * device special file, we ignore rofs. */ - if (!(acc & MAY_LOCAL_ACCESS)) - if (acc & (MAY_WRITE | MAY_SATTR | MAY_TRUNC)) { + if (!(acc & NFSD_MAY_LOCAL_ACCESS)) + if (acc & (NFSD_MAY_WRITE | NFSD_MAY_SATTR | NFSD_MAY_TRUNC)) { if (exp_rdonly(rqstp, exp) || __mnt_is_readonly(exp->ex_path.mnt)) return nfserr_rofs; - if (/* (acc & MAY_WRITE) && */ IS_IMMUTABLE(inode)) + if (/* (acc & NFSD_MAY_WRITE) && */ IS_IMMUTABLE(inode)) return nfserr_perm; } - if ((acc & MAY_TRUNC) && IS_APPEND(inode)) + if ((acc & NFSD_MAY_TRUNC) && IS_APPEND(inode)) return nfserr_perm; - if (acc & MAY_LOCK) { + if (acc & NFSD_MAY_LOCK) { /* If we cannot rely on authentication in NLM requests, * just allow locks, otherwise require read permission, or * ownership @@ -1939,7 +1941,7 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp, if (exp->ex_flags & NFSEXP_NOAUTHNLM) return 0; else - acc = MAY_READ | MAY_OWNER_OVERRIDE; + acc = NFSD_MAY_READ | NFSD_MAY_OWNER_OVERRIDE; } /* * The file owner always gets access permission for accesses that @@ -1955,15 +1957,16 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp, * We must trust the client to do permission checking - using "ACCESS" * with NFSv3. */ - if ((acc & MAY_OWNER_OVERRIDE) && + if ((acc & NFSD_MAY_OWNER_OVERRIDE) && inode->i_uid == current->fsuid) return 0; + /* This assumes NFSD_MAY_{READ,WRITE,EXEC} == MAY_{READ,WRITE,EXEC} */ err = permission(inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC), NULL); /* Allow read access to binaries even when mode 111 */ if (err == -EACCES && S_ISREG(inode->i_mode) && - acc == (MAY_READ | MAY_OWNER_OVERRIDE)) + acc == (NFSD_MAY_READ | NFSD_MAY_OWNER_OVERRIDE)) err = permission(inode, MAY_EXEC, NULL); return err? nfserrno(err) : 0; diff --git a/include/linux/nfsd/nfsd.h b/include/linux/nfsd/nfsd.h index 88d85b964429..a2861d95ecc3 100644 --- a/include/linux/nfsd/nfsd.h +++ b/include/linux/nfsd/nfsd.h @@ -28,20 +28,20 @@ #define NFSD_SUPPORTED_MINOR_VERSION 0 /* - * Special flags for nfsd_permission. These must be different from MAY_READ, - * MAY_WRITE, and MAY_EXEC. + * Flags for nfsd_permission */ -#define MAY_NOP 0 -#define MAY_SATTR 8 -#define MAY_TRUNC 16 -#define MAY_LOCK 32 -#define MAY_OWNER_OVERRIDE 64 -#define MAY_LOCAL_ACCESS 128 /* IRIX doing local access check on device special file*/ -#if (MAY_SATTR | MAY_TRUNC | MAY_LOCK | MAY_OWNER_OVERRIDE | MAY_LOCAL_ACCESS) & (MAY_READ | MAY_WRITE | MAY_EXEC) -# error "please use a different value for MAY_SATTR or MAY_TRUNC or MAY_LOCK or MAY_LOCAL_ACCESS or MAY_OWNER_OVERRIDE." -#endif -#define MAY_CREATE (MAY_EXEC|MAY_WRITE) -#define MAY_REMOVE (MAY_EXEC|MAY_WRITE|MAY_TRUNC) +#define NFSD_MAY_NOP 0 +#define NFSD_MAY_EXEC 1 /* == MAY_EXEC */ +#define NFSD_MAY_WRITE 2 /* == MAY_WRITE */ +#define NFSD_MAY_READ 4 /* == MAY_READ */ +#define NFSD_MAY_SATTR 8 +#define NFSD_MAY_TRUNC 16 +#define NFSD_MAY_LOCK 32 +#define NFSD_MAY_OWNER_OVERRIDE 64 +#define NFSD_MAY_LOCAL_ACCESS 128 /* IRIX doing local access check on device special file*/ + +#define NFSD_MAY_CREATE (NFSD_MAY_EXEC|NFSD_MAY_WRITE) +#define NFSD_MAY_REMOVE (NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC) /* * Callback function for readdir -- cgit v1.2.3 From d00953a53e9a2edbe005c1e596f1e96a8a293401 Mon Sep 17 00:00:00 2001 From: Kevin Coffman Date: Wed, 30 Apr 2008 12:45:53 -0400 Subject: gss_krb5: create a define for token header size and clean up ptr location cleanup: Document token header size with a #define instead of open-coding it. Don't needlessly increment "ptr" past the beginning of the header which makes the values passed to functions more understandable and eliminates the need for extra "krb5_hdr" pointer. Clean up some intersecting white-space issues flagged by checkpatch.pl. This leaves the checksum length hard-coded at 8 for DES. A later patch cleans that up. Signed-off-by: Kevin Coffman Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/gss_krb5.h | 3 +++ net/sunrpc/auth_gss/gss_krb5_seal.c | 26 +++++++++--------- net/sunrpc/auth_gss/gss_krb5_unseal.c | 16 +++++------ net/sunrpc/auth_gss/gss_krb5_wrap.c | 50 +++++++++++++++++------------------ 4 files changed, 49 insertions(+), 46 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/gss_krb5.h b/include/linux/sunrpc/gss_krb5.h index a10f1fb0bf7c..e7bbdba474d5 100644 --- a/include/linux/sunrpc/gss_krb5.h +++ b/include/linux/sunrpc/gss_krb5.h @@ -51,6 +51,9 @@ struct krb5_ctx { extern spinlock_t krb5_seq_lock; +/* The length of the Kerberos GSS token header */ +#define GSS_KRB5_TOK_HDR_LEN (16) + #define KG_TOK_MIC_MSG 0x0101 #define KG_TOK_WRAP_MSG 0x0201 diff --git a/net/sunrpc/auth_gss/gss_krb5_seal.c b/net/sunrpc/auth_gss/gss_krb5_seal.c index 5f1d36dfbcf7..b8f42ef7178e 100644 --- a/net/sunrpc/auth_gss/gss_krb5_seal.c +++ b/net/sunrpc/auth_gss/gss_krb5_seal.c @@ -78,7 +78,7 @@ gss_get_mic_kerberos(struct gss_ctx *gss_ctx, struct xdr_buf *text, struct krb5_ctx *ctx = gss_ctx->internal_ctx_id; char cksumdata[16]; struct xdr_netobj md5cksum = {.len = 0, .data = cksumdata}; - unsigned char *ptr, *krb5_hdr, *msg_start; + unsigned char *ptr, *msg_start; s32 now; u32 seq_send; @@ -87,36 +87,36 @@ gss_get_mic_kerberos(struct gss_ctx *gss_ctx, struct xdr_buf *text, now = get_seconds(); - token->len = g_token_size(&ctx->mech_used, 24); + token->len = g_token_size(&ctx->mech_used, GSS_KRB5_TOK_HDR_LEN + 8); ptr = token->data; - g_make_token_header(&ctx->mech_used, 24, &ptr); + g_make_token_header(&ctx->mech_used, GSS_KRB5_TOK_HDR_LEN + 8, &ptr); - *ptr++ = (unsigned char) ((KG_TOK_MIC_MSG>>8)&0xff); - *ptr++ = (unsigned char) (KG_TOK_MIC_MSG&0xff); + /* ptr now at header described in rfc 1964, section 1.2.1: */ + ptr[0] = (unsigned char) ((KG_TOK_MIC_MSG >> 8) & 0xff); + ptr[1] = (unsigned char) (KG_TOK_MIC_MSG & 0xff); - /* ptr now at byte 2 of header described in rfc 1964, section 1.2.1: */ - krb5_hdr = ptr - 2; - msg_start = krb5_hdr + 24; + msg_start = ptr + GSS_KRB5_TOK_HDR_LEN + 8; - *(__be16 *)(krb5_hdr + 2) = htons(SGN_ALG_DES_MAC_MD5); - memset(krb5_hdr + 4, 0xff, 4); + *(__be16 *)(ptr + 2) = htons(SGN_ALG_DES_MAC_MD5); + memset(ptr + 4, 0xff, 4); - if (make_checksum("md5", krb5_hdr, 8, text, 0, &md5cksum)) + if (make_checksum("md5", ptr, 8, text, 0, &md5cksum)) return GSS_S_FAILURE; if (krb5_encrypt(ctx->seq, NULL, md5cksum.data, md5cksum.data, md5cksum.len)) return GSS_S_FAILURE; - memcpy(krb5_hdr + 16, md5cksum.data + md5cksum.len - 8, 8); + memcpy(ptr + GSS_KRB5_TOK_HDR_LEN, md5cksum.data + md5cksum.len - 8, 8); spin_lock(&krb5_seq_lock); seq_send = ctx->seq_send++; spin_unlock(&krb5_seq_lock); if (krb5_make_seq_num(ctx->seq, ctx->initiate ? 0 : 0xff, - seq_send, krb5_hdr + 16, krb5_hdr + 8)) + seq_send, ptr + GSS_KRB5_TOK_HDR_LEN, + ptr + 8)) return GSS_S_FAILURE; return (ctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE; diff --git a/net/sunrpc/auth_gss/gss_krb5_unseal.c b/net/sunrpc/auth_gss/gss_krb5_unseal.c index d91a5d004803..066ec73c84d6 100644 --- a/net/sunrpc/auth_gss/gss_krb5_unseal.c +++ b/net/sunrpc/auth_gss/gss_krb5_unseal.c @@ -92,30 +92,30 @@ gss_verify_mic_kerberos(struct gss_ctx *gss_ctx, read_token->len)) return GSS_S_DEFECTIVE_TOKEN; - if ((*ptr++ != ((KG_TOK_MIC_MSG>>8)&0xff)) || - (*ptr++ != ( KG_TOK_MIC_MSG &0xff)) ) + if ((ptr[0] != ((KG_TOK_MIC_MSG >> 8) & 0xff)) || + (ptr[1] != (KG_TOK_MIC_MSG & 0xff))) return GSS_S_DEFECTIVE_TOKEN; /* XXX sanity-check bodysize?? */ - signalg = ptr[0] + (ptr[1] << 8); + signalg = ptr[2] + (ptr[3] << 8); if (signalg != SGN_ALG_DES_MAC_MD5) return GSS_S_DEFECTIVE_TOKEN; - sealalg = ptr[2] + (ptr[3] << 8); + sealalg = ptr[4] + (ptr[5] << 8); if (sealalg != SEAL_ALG_NONE) return GSS_S_DEFECTIVE_TOKEN; - if ((ptr[4] != 0xff) || (ptr[5] != 0xff)) + if ((ptr[6] != 0xff) || (ptr[7] != 0xff)) return GSS_S_DEFECTIVE_TOKEN; - if (make_checksum("md5", ptr - 2, 8, message_buffer, 0, &md5cksum)) + if (make_checksum("md5", ptr, 8, message_buffer, 0, &md5cksum)) return GSS_S_FAILURE; if (krb5_encrypt(ctx->seq, NULL, md5cksum.data, md5cksum.data, 16)) return GSS_S_FAILURE; - if (memcmp(md5cksum.data + 8, ptr + 14, 8)) + if (memcmp(md5cksum.data + 8, ptr + GSS_KRB5_TOK_HDR_LEN, 8)) return GSS_S_BAD_SIG; /* it got through unscathed. Make sure the context is unexpired */ @@ -127,7 +127,7 @@ gss_verify_mic_kerberos(struct gss_ctx *gss_ctx, /* do sequencing checks */ - if (krb5_get_seq_num(ctx->seq, ptr + 14, ptr + 6, &direction, &seqnum)) + if (krb5_get_seq_num(ctx->seq, ptr + GSS_KRB5_TOK_HDR_LEN, ptr + 8, &direction, &seqnum)) return GSS_S_FAILURE; if ((ctx->initiate && direction != 0xff) || diff --git a/net/sunrpc/auth_gss/gss_krb5_wrap.c b/net/sunrpc/auth_gss/gss_krb5_wrap.c index b00b1b426301..283cb25c6237 100644 --- a/net/sunrpc/auth_gss/gss_krb5_wrap.c +++ b/net/sunrpc/auth_gss/gss_krb5_wrap.c @@ -122,7 +122,7 @@ gss_wrap_kerberos(struct gss_ctx *ctx, int offset, char cksumdata[16]; struct xdr_netobj md5cksum = {.len = 0, .data = cksumdata}; int blocksize = 0, plainlen; - unsigned char *ptr, *krb5_hdr, *msg_start; + unsigned char *ptr, *msg_start; s32 now; int headlen; struct page **tmp_pages; @@ -149,26 +149,26 @@ gss_wrap_kerberos(struct gss_ctx *ctx, int offset, buf->len += headlen; BUG_ON((buf->len - offset - headlen) % blocksize); - g_make_token_header(&kctx->mech_used, 24 + plainlen, &ptr); + g_make_token_header(&kctx->mech_used, + GSS_KRB5_TOK_HDR_LEN + 8 + plainlen, &ptr); - *ptr++ = (unsigned char) ((KG_TOK_WRAP_MSG>>8)&0xff); - *ptr++ = (unsigned char) (KG_TOK_WRAP_MSG&0xff); + /* ptr now at header described in rfc 1964, section 1.2.1: */ + ptr[0] = (unsigned char) ((KG_TOK_WRAP_MSG >> 8) & 0xff); + ptr[1] = (unsigned char) (KG_TOK_WRAP_MSG & 0xff); - /* ptr now at byte 2 of header described in rfc 1964, section 1.2.1: */ - krb5_hdr = ptr - 2; - msg_start = krb5_hdr + 24; + msg_start = ptr + 24; - *(__be16 *)(krb5_hdr + 2) = htons(SGN_ALG_DES_MAC_MD5); - memset(krb5_hdr + 4, 0xff, 4); - *(__be16 *)(krb5_hdr + 4) = htons(SEAL_ALG_DES); + *(__be16 *)(ptr + 2) = htons(SGN_ALG_DES_MAC_MD5); + memset(ptr + 4, 0xff, 4); + *(__be16 *)(ptr + 4) = htons(SEAL_ALG_DES); make_confounder(msg_start, blocksize); /* XXXJBF: UGH!: */ tmp_pages = buf->pages; buf->pages = pages; - if (make_checksum("md5", krb5_hdr, 8, buf, + if (make_checksum("md5", ptr, 8, buf, offset + headlen - blocksize, &md5cksum)) return GSS_S_FAILURE; buf->pages = tmp_pages; @@ -176,7 +176,7 @@ gss_wrap_kerberos(struct gss_ctx *ctx, int offset, if (krb5_encrypt(kctx->seq, NULL, md5cksum.data, md5cksum.data, md5cksum.len)) return GSS_S_FAILURE; - memcpy(krb5_hdr + 16, md5cksum.data + md5cksum.len - 8, 8); + memcpy(ptr + GSS_KRB5_TOK_HDR_LEN, md5cksum.data + md5cksum.len - 8, 8); spin_lock(&krb5_seq_lock); seq_send = kctx->seq_send++; @@ -185,7 +185,7 @@ gss_wrap_kerberos(struct gss_ctx *ctx, int offset, /* XXX would probably be more efficient to compute checksum * and encrypt at the same time: */ if ((krb5_make_seq_num(kctx->seq, kctx->initiate ? 0 : 0xff, - seq_send, krb5_hdr + 16, krb5_hdr + 8))) + seq_send, ptr + GSS_KRB5_TOK_HDR_LEN, ptr + 8))) return GSS_S_FAILURE; if (gss_encrypt_xdr_buf(kctx->enc, buf, offset + headlen - blocksize, @@ -219,38 +219,38 @@ gss_unwrap_kerberos(struct gss_ctx *ctx, int offset, struct xdr_buf *buf) buf->len - offset)) return GSS_S_DEFECTIVE_TOKEN; - if ((*ptr++ != ((KG_TOK_WRAP_MSG>>8)&0xff)) || - (*ptr++ != (KG_TOK_WRAP_MSG &0xff)) ) + if ((ptr[0] != ((KG_TOK_WRAP_MSG >> 8) & 0xff)) || + (ptr[1] != (KG_TOK_WRAP_MSG & 0xff))) return GSS_S_DEFECTIVE_TOKEN; /* XXX sanity-check bodysize?? */ /* get the sign and seal algorithms */ - signalg = ptr[0] + (ptr[1] << 8); + signalg = ptr[2] + (ptr[3] << 8); if (signalg != SGN_ALG_DES_MAC_MD5) return GSS_S_DEFECTIVE_TOKEN; - sealalg = ptr[2] + (ptr[3] << 8); + sealalg = ptr[4] + (ptr[5] << 8); if (sealalg != SEAL_ALG_DES) return GSS_S_DEFECTIVE_TOKEN; - if ((ptr[4] != 0xff) || (ptr[5] != 0xff)) + if ((ptr[6] != 0xff) || (ptr[7] != 0xff)) return GSS_S_DEFECTIVE_TOKEN; if (gss_decrypt_xdr_buf(kctx->enc, buf, - ptr + 22 - (unsigned char *)buf->head[0].iov_base)) + ptr + GSS_KRB5_TOK_HDR_LEN + 8 - (unsigned char *)buf->head[0].iov_base)) return GSS_S_DEFECTIVE_TOKEN; - if (make_checksum("md5", ptr - 2, 8, buf, - ptr + 22 - (unsigned char *)buf->head[0].iov_base, &md5cksum)) + if (make_checksum("md5", ptr, 8, buf, + ptr + GSS_KRB5_TOK_HDR_LEN + 8 - (unsigned char *)buf->head[0].iov_base, &md5cksum)) return GSS_S_FAILURE; if (krb5_encrypt(kctx->seq, NULL, md5cksum.data, md5cksum.data, md5cksum.len)) return GSS_S_FAILURE; - if (memcmp(md5cksum.data + 8, ptr + 14, 8)) + if (memcmp(md5cksum.data + 8, ptr + GSS_KRB5_TOK_HDR_LEN, 8)) return GSS_S_BAD_SIG; /* it got through unscathed. Make sure the context is unexpired */ @@ -262,8 +262,8 @@ gss_unwrap_kerberos(struct gss_ctx *ctx, int offset, struct xdr_buf *buf) /* do sequencing checks */ - if (krb5_get_seq_num(kctx->seq, ptr + 14, ptr + 6, &direction, - &seqnum)) + if (krb5_get_seq_num(kctx->seq, ptr + GSS_KRB5_TOK_HDR_LEN, ptr + 8, + &direction, &seqnum)) return GSS_S_BAD_SIG; if ((kctx->initiate && direction != 0xff) || @@ -274,7 +274,7 @@ gss_unwrap_kerberos(struct gss_ctx *ctx, int offset, struct xdr_buf *buf) * better to copy and encrypt at the same time. */ blocksize = crypto_blkcipher_blocksize(kctx->enc); - data_start = ptr + 22 + blocksize; + data_start = ptr + GSS_KRB5_TOK_HDR_LEN + 8 + blocksize; orig_start = buf->head[0].iov_base + offset; data_len = (buf->head[0].iov_base + buf->head[0].iov_len) - data_start; memmove(orig_start, data_start, data_len); -- cgit v1.2.3 From 34f80b04f325078ff21123579343d99756ad8d0e Mon Sep 17 00:00:00 2001 From: Eilon Greenstein Date: Mon, 23 Jun 2008 20:33:01 -0700 Subject: bnx2x: Add support for BCM57711 HW Supporting the 57711 and 57711E - refers to in the code as E1H. The 57710 is referred to as E1. To support the new members in the family, the bnx2x structure was divided to 3 parts: common, port and function. These changes caused some rearrangement in the bnx2x.h file. A set of accessories macros were added to make access to the bnx2x structure more readable Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/Kconfig | 1 + drivers/net/bnx2x.h | 739 ++++---- drivers/net/bnx2x_fw_defs.h | 483 ++++-- drivers/net/bnx2x_hsi.h | 708 +++++--- drivers/net/bnx2x_init.h | 20 +- drivers/net/bnx2x_link.c | 11 +- drivers/net/bnx2x_main.c | 3919 +++++++++++++++++++++++++++---------------- drivers/net/bnx2x_reg.h | 66 +- include/linux/pci_ids.h | 2 + 9 files changed, 3774 insertions(+), 2175 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 0e0f6696ccac..287d0873c60d 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2600,6 +2600,7 @@ config BNX2X tristate "Broadcom NetXtremeII 10Gb support" depends on PCI select ZLIB_INFLATE + select LIBCRC32C help This driver supports Broadcom NetXtremeII 10 gigabit Ethernet cards. To compile this driver as a module, choose M here: the module diff --git a/drivers/net/bnx2x.h b/drivers/net/bnx2x.h index 0979ca0ae408..e08b9439a933 100644 --- a/drivers/net/bnx2x.h +++ b/drivers/net/bnx2x.h @@ -14,39 +14,46 @@ #ifndef BNX2X_H #define BNX2X_H +/* compilation time flags */ + +/* define this to make the driver freeze on error to allow getting debug info + * (you will need to reboot afterwards) */ +/* #define BNX2X_STOP_ON_ERROR */ + /* error/debug prints */ -#define DRV_MODULE_NAME "bnx2x" -#define PFX DRV_MODULE_NAME ": " +#define DRV_MODULE_NAME "bnx2x" +#define PFX DRV_MODULE_NAME ": " /* for messages that are currently off */ -#define BNX2X_MSG_OFF 0 -#define BNX2X_MSG_MCP 0x10000 /* was: NETIF_MSG_HW */ -#define BNX2X_MSG_STATS 0x20000 /* was: NETIF_MSG_TIMER */ -#define NETIF_MSG_NVM 0x40000 /* was: NETIF_MSG_HW */ -#define NETIF_MSG_DMAE 0x80000 /* was: NETIF_MSG_HW */ +#define BNX2X_MSG_OFF 0 +#define BNX2X_MSG_MCP 0x010000 /* was: NETIF_MSG_HW */ +#define BNX2X_MSG_STATS 0x020000 /* was: NETIF_MSG_TIMER */ +#define BNX2X_MSG_NVM 0x040000 /* was: NETIF_MSG_HW */ +#define BNX2X_MSG_DMAE 0x080000 /* was: NETIF_MSG_HW */ #define BNX2X_MSG_SP 0x100000 /* was: NETIF_MSG_INTR */ #define BNX2X_MSG_FP 0x200000 /* was: NETIF_MSG_INTR */ -#define DP_LEVEL KERN_NOTICE /* was: KERN_DEBUG */ +#define DP_LEVEL KERN_NOTICE /* was: KERN_DEBUG */ /* regular debug print */ #define DP(__mask, __fmt, __args...) do { \ if (bp->msglevel & (__mask)) \ - printk(DP_LEVEL "[%s:%d(%s)]" __fmt, __FUNCTION__, \ - __LINE__, bp->dev?(bp->dev->name):"?", ##__args); \ + printk(DP_LEVEL "[%s:%d(%s)]" __fmt, __func__, __LINE__, \ + bp->dev?(bp->dev->name):"?", ##__args); \ } while (0) -/* for errors (never masked) */ -#define BNX2X_ERR(__fmt, __args...) do { \ - printk(KERN_ERR "[%s:%d(%s)]" __fmt, __FUNCTION__, \ - __LINE__, bp->dev?(bp->dev->name):"?", ##__args); \ +/* errors debug print */ +#define BNX2X_DBG_ERR(__fmt, __args...) do { \ + if (bp->msglevel & NETIF_MSG_PROBE) \ + printk(KERN_ERR "[%s:%d(%s)]" __fmt, __func__, __LINE__, \ + bp->dev?(bp->dev->name):"?", ##__args); \ } while (0) -/* for logging (never masked) */ -#define BNX2X_LOG(__fmt, __args...) do { \ - printk(KERN_NOTICE "[%s:%d(%s)]" __fmt, __FUNCTION__, \ - __LINE__, bp->dev?(bp->dev->name):"?", ##__args); \ +/* for errors (never masked) */ +#define BNX2X_ERR(__fmt, __args...) do { \ + printk(KERN_ERR "[%s:%d(%s)]" __fmt, __func__, __LINE__, \ + bp->dev?(bp->dev->name):"?", ##__args); \ } while (0) /* before we have a dev->name use dev_info() */ @@ -60,7 +67,7 @@ #define bnx2x_panic() do { \ bp->panic = 1; \ BNX2X_ERR("driver assert\n"); \ - bnx2x_disable_int(bp); \ + bnx2x_int_disable(bp); \ bnx2x_panic_dump(bp); \ } while (0) #else @@ -71,24 +78,29 @@ #endif -#define U64_LO(x) (((u64)x) & 0xffffffff) -#define U64_HI(x) (((u64)x) >> 32) -#define HILO_U64(hi, lo) (((u64)hi << 32) + lo) +#ifdef NETIF_F_HW_VLAN_TX +#define BCM_VLAN 1 +#endif + +#define U64_LO(x) (u32)(((u64)(x)) & 0xffffffff) +#define U64_HI(x) (u32)(((u64)(x)) >> 32) +#define HILO_U64(hi, lo) ((((u64)(hi)) << 32) + (lo)) -#define REG_ADDR(bp, offset) (bp->regview + offset) -#define REG_RD(bp, offset) readl(REG_ADDR(bp, offset)) -#define REG_RD8(bp, offset) readb(REG_ADDR(bp, offset)) -#define REG_RD64(bp, offset) readq(REG_ADDR(bp, offset)) +#define REG_ADDR(bp, offset) (bp->regview + offset) -#define REG_WR(bp, offset, val) writel((u32)val, REG_ADDR(bp, offset)) +#define REG_RD(bp, offset) readl(REG_ADDR(bp, offset)) +#define REG_RD8(bp, offset) readb(REG_ADDR(bp, offset)) +#define REG_RD64(bp, offset) readq(REG_ADDR(bp, offset)) + +#define REG_WR(bp, offset, val) writel((u32)val, REG_ADDR(bp, offset)) #define REG_WR8(bp, offset, val) writeb((u8)val, REG_ADDR(bp, offset)) -#define REG_WR16(bp, offset, val) writew((u16)val, REG_ADDR(bp, offset)) -#define REG_WR32(bp, offset, val) REG_WR(bp, offset, val) +#define REG_WR16(bp, offset, val) writew((u16)val, REG_ADDR(bp, offset)) +#define REG_WR32(bp, offset, val) REG_WR(bp, offset, val) -#define REG_RD_IND(bp, offset) bnx2x_reg_rd_ind(bp, offset) -#define REG_WR_IND(bp, offset, val) bnx2x_reg_wr_ind(bp, offset, val) +#define REG_RD_IND(bp, offset) bnx2x_reg_rd_ind(bp, offset) +#define REG_WR_IND(bp, offset, val) bnx2x_reg_wr_ind(bp, offset, val) #define REG_RD_DMAE(bp, offset, valp, len32) \ do { \ @@ -96,28 +108,28 @@ memcpy(valp, bnx2x_sp(bp, wb_data[0]), len32 * 4); \ } while (0) -#define REG_WR_DMAE(bp, offset, val, len32) \ +#define REG_WR_DMAE(bp, offset, valp, len32) \ do { \ - memcpy(bnx2x_sp(bp, wb_data[0]), val, len32 * 4); \ + memcpy(bnx2x_sp(bp, wb_data[0]), valp, len32 * 4); \ bnx2x_write_dmae(bp, bnx2x_sp_mapping(bp, wb_data), \ offset, len32); \ } while (0) -#define SHMEM_RD(bp, type) \ - REG_RD(bp, bp->shmem_base + offsetof(struct shmem_region, type)) -#define SHMEM_WR(bp, type, val) \ - REG_WR(bp, bp->shmem_base + offsetof(struct shmem_region, type), val) +#define SHMEM_ADDR(bp, field) (bp->common.shmem_base + \ + offsetof(struct shmem_region, field)) +#define SHMEM_RD(bp, field) REG_RD(bp, SHMEM_ADDR(bp, field)) +#define SHMEM_WR(bp, field, val) REG_WR(bp, SHMEM_ADDR(bp, field), val) #define NIG_WR(reg, val) REG_WR(bp, reg, val) -#define EMAC_WR(reg, val) REG_WR(bp, emac_base + reg, val) -#define BMAC_WR(reg, val) REG_WR(bp, GRCBASE_NIG + bmac_addr + reg, val) +#define EMAC_WR(reg, val) REG_WR(bp, emac_base + reg, val) +#define BMAC_WR(reg, val) REG_WR(bp, GRCBASE_NIG + bmac_addr + reg, val) -#define for_each_queue(bp, var) for (var = 0; var < bp->num_queues; var++) +#define for_each_queue(bp, var) for (var = 0; var < bp->num_queues; var++) #define for_each_nondefault_queue(bp, var) \ for (var = 1; var < bp->num_queues; var++) -#define is_multi(bp) (bp->num_queues > 1) +#define is_multi(bp) (bp->num_queues > 1) struct regp { @@ -358,210 +370,122 @@ struct bnx2x_eth_stats { u32 number_of_bugs_found_in_stats_spec; /* just kidding */ }; -#define MAC_STX_NA 0xffffffff - -#ifdef BNX2X_MULTI -#define MAX_CONTEXT 16 -#else -#define MAX_CONTEXT 1 -#endif - -union cdu_context { - struct eth_context eth; - char pad[1024]; -}; - -#define MAX_DMAE_C 5 - -/* DMA memory not used in fastpath */ -struct bnx2x_slowpath { - union cdu_context context[MAX_CONTEXT]; - struct eth_stats_query fw_stats; - struct mac_configuration_cmd mac_config; - struct mac_configuration_cmd mcast_config; - - /* used by dmae command executer */ - struct dmae_command dmae[MAX_DMAE_C]; - - union mac_stats mac_stats; - struct nig_stats nig; - struct bnx2x_eth_stats eth_stats; - - u32 wb_comp; -#define BNX2X_WB_COMP_VAL 0xe0d0d0ae - u32 wb_data[4]; -}; - -#define bnx2x_sp(bp, var) (&bp->slowpath->var) #define bnx2x_sp_check(bp, var) ((bp->slowpath) ? (&bp->slowpath->var) : NULL) -#define bnx2x_sp_mapping(bp, var) \ - (bp->slowpath_mapping + offsetof(struct bnx2x_slowpath, var)) - - struct sw_rx_bd { - struct sk_buff *skb; + struct sk_buff *skb; DECLARE_PCI_UNMAP_ADDR(mapping) }; struct sw_tx_bd { - struct sk_buff *skb; - u16 first_bd; + struct sk_buff *skb; + u16 first_bd; }; struct bnx2x_fastpath { - struct napi_struct napi; + struct napi_struct napi; struct host_status_block *status_blk; - dma_addr_t status_blk_mapping; + dma_addr_t status_blk_mapping; - struct eth_tx_db_data *hw_tx_prods; - dma_addr_t tx_prods_mapping; + struct eth_tx_db_data *hw_tx_prods; + dma_addr_t tx_prods_mapping; - struct sw_tx_bd *tx_buf_ring; + struct sw_tx_bd *tx_buf_ring; struct eth_tx_bd *tx_desc_ring; - dma_addr_t tx_desc_mapping; + dma_addr_t tx_desc_mapping; struct sw_rx_bd *rx_buf_ring; struct eth_rx_bd *rx_desc_ring; - dma_addr_t rx_desc_mapping; + dma_addr_t rx_desc_mapping; union eth_rx_cqe *rx_comp_ring; - dma_addr_t rx_comp_mapping; - - int state; -#define BNX2X_FP_STATE_CLOSED 0 -#define BNX2X_FP_STATE_IRQ 0x80000 -#define BNX2X_FP_STATE_OPENING 0x90000 -#define BNX2X_FP_STATE_OPEN 0xa0000 -#define BNX2X_FP_STATE_HALTING 0xb0000 -#define BNX2X_FP_STATE_HALTED 0xc0000 - - int index; - - u16 tx_pkt_prod; - u16 tx_pkt_cons; - u16 tx_bd_prod; - u16 tx_bd_cons; - u16 *tx_cons_sb; - - u16 fp_c_idx; - u16 fp_u_idx; - - u16 rx_bd_prod; - u16 rx_bd_cons; - u16 rx_comp_prod; - u16 rx_comp_cons; - u16 *rx_cons_sb; - - unsigned long tx_pkt, + dma_addr_t rx_comp_mapping; + + int state; +#define BNX2X_FP_STATE_CLOSED 0 +#define BNX2X_FP_STATE_IRQ 0x80000 +#define BNX2X_FP_STATE_OPENING 0x90000 +#define BNX2X_FP_STATE_OPEN 0xa0000 +#define BNX2X_FP_STATE_HALTING 0xb0000 +#define BNX2X_FP_STATE_HALTED 0xc0000 + + u8 index; /* number in fp array */ + u8 cl_id; /* eth client id */ + u8 sb_id; /* status block number in HW */ +#define FP_IDX(fp) (fp->index) +#define FP_CL_ID(fp) (fp->cl_id) +#define BP_CL_ID(bp) (bp->fp[0].cl_id) +#define FP_SB_ID(fp) (fp->sb_id) +#define CNIC_SB_ID 0 + + u16 tx_pkt_prod; + u16 tx_pkt_cons; + u16 tx_bd_prod; + u16 tx_bd_cons; + u16 *tx_cons_sb; + + u16 fp_c_idx; + u16 fp_u_idx; + + u16 rx_bd_prod; + u16 rx_bd_cons; + u16 rx_comp_prod; + u16 rx_comp_cons; + u16 *rx_cons_sb; + + unsigned long tx_pkt, rx_pkt, rx_calls; - struct bnx2x *bp; /* parent */ -}; - -#define bnx2x_fp(bp, nr, var) (bp->fp[nr].var) - - -/* attn group wiring */ -#define MAX_DYNAMIC_ATTN_GRPS 8 - -struct attn_route { - u32 sig[4]; + struct bnx2x *bp; /* parent */ }; -struct bnx2x { - /* Fields used in the tx and intr/napi performance paths - * are grouped together in the beginning of the structure - */ - struct bnx2x_fastpath *fp; - void __iomem *regview; - void __iomem *doorbells; - - struct net_device *dev; - struct pci_dev *pdev; - - atomic_t intr_sem; - struct msix_entry msix_table[MAX_CONTEXT+1]; - - int tx_ring_size; +#define bnx2x_fp(bp, nr, var) (bp->fp[nr].var) +/* This is needed for determening of last_max */ +#define SUB_S16(a, b) (s16)((s16)(a) - (s16)(b)) -#ifdef BCM_VLAN - struct vlan_group *vlgrp; -#endif - - u32 rx_csum; - u32 rx_offset; - u32 rx_buf_use_size; /* useable size */ - u32 rx_buf_size; /* with alignment */ -#define ETH_OVREHEAD (ETH_HLEN + 8) /* 8 for CRC + VLAN */ -#define ETH_MIN_PACKET_SIZE 60 -#define ETH_MAX_PACKET_SIZE 1500 -#define ETH_MAX_JUMBO_PACKET_SIZE 9600 +/* stuff added to make the code fit 80Col */ - struct host_def_status_block *def_status_blk; -#define DEF_SB_ID 16 - u16 def_c_idx; - u16 def_u_idx; - u16 def_t_idx; - u16 def_x_idx; - u16 def_att_idx; - u32 attn_state; - struct attn_route attn_group[MAX_DYNAMIC_ATTN_GRPS]; - u32 aeu_mask; - u32 nig_mask; +#define CQE_TYPE(cqe_fp_flags) ((cqe_fp_flags) & ETH_FAST_PATH_RX_CQE_TYPE) - /* slow path ring */ - struct eth_spe *spq; - dma_addr_t spq_mapping; - u16 spq_prod_idx; - struct eth_spe *spq_prod_bd; - struct eth_spe *spq_last_bd; - u16 *dsb_sp_prod; - u16 spq_left; /* serialize spq */ - spinlock_t spq_lock; - - /* Flag for marking that there is either - * STAT_QUERY or CFC DELETE ramrod pending - */ - u8 stat_pending; +#define ETH_RX_ERROR_FALGS (ETH_FAST_PATH_RX_CQE_PHY_DECODE_ERR_FLG | \ + ETH_FAST_PATH_RX_CQE_IP_BAD_XSUM_FLG | \ + ETH_FAST_PATH_RX_CQE_L4_BAD_XSUM_FLG) - /* End of fields used in the performance code paths */ - int panic; - int msglevel; +#define U_SB_ETH_RX_CQ_INDEX HC_INDEX_U_ETH_RX_CQ_CONS +#define U_SB_ETH_RX_BD_INDEX HC_INDEX_U_ETH_RX_BD_CONS +#define C_SB_ETH_TX_CQ_INDEX HC_INDEX_C_ETH_TX_CQ_CONS - u32 flags; -#define PCIX_FLAG 1 -#define PCI_32BIT_FLAG 2 -#define ONE_TDMA_FLAG 4 /* no longer used */ -#define NO_WOL_FLAG 8 -#define USING_DAC_FLAG 0x10 -#define USING_MSIX_FLAG 0x20 -#define ASF_ENABLE_FLAG 0x40 +#define BNX2X_RX_SB_INDEX \ + (&fp->status_blk->u_status_block.index_values[U_SB_ETH_RX_CQ_INDEX]) - int port; +#define BNX2X_RX_SB_BD_INDEX \ + (&fp->status_blk->u_status_block.index_values[U_SB_ETH_RX_BD_INDEX]) - int pm_cap; - int pcie_cap; +#define BNX2X_RX_SB_INDEX_NUM \ + (((U_SB_ETH_RX_CQ_INDEX << \ + USTORM_ETH_ST_CONTEXT_CONFIG_CQE_SB_INDEX_NUMBER_SHIFT) & \ + USTORM_ETH_ST_CONTEXT_CONFIG_CQE_SB_INDEX_NUMBER) | \ + ((U_SB_ETH_RX_BD_INDEX << \ + USTORM_ETH_ST_CONTEXT_CONFIG_BD_SB_INDEX_NUMBER_SHIFT) & \ + USTORM_ETH_ST_CONTEXT_CONFIG_BD_SB_INDEX_NUMBER)) - struct work_struct sp_task; - struct work_struct reset_task; +#define BNX2X_TX_SB_INDEX \ + (&fp->status_blk->c_status_block.index_values[C_SB_ETH_TX_CQ_INDEX]) - struct timer_list timer; - int timer_interval; - int current_interval; +/* common */ - u32 shmem_base; +struct bnx2x_common { u32 chip_id; /* chip num:16-31, rev:12-15, metal:4-11, bond_id:0-3 */ -#define CHIP_ID(bp) (bp->chip_id & 0xfffffff0) +#define CHIP_ID(bp) (bp->common.chip_id & 0xfffffff0) -#define CHIP_NUM(bp) (bp->chip_id >> 16) +#define CHIP_NUM(bp) (bp->common.chip_id >> 16) #define CHIP_NUM_57710 0x164e #define CHIP_NUM_57711 0x164f #define CHIP_NUM_57711E 0x1650 @@ -572,7 +496,7 @@ struct bnx2x { CHIP_IS_57711E(bp)) #define IS_E1H_OFFSET CHIP_IS_E1H(bp) -#define CHIP_REV(bp) (bp->chip_id & 0x0000f000) +#define CHIP_REV(bp) (bp->common.chip_id & 0x0000f000) #define CHIP_REV_Ax 0x00000000 /* assume maximum 5 revisions */ #define CHIP_REV_IS_SLOW(bp) (CHIP_REV(bp) > 0x00005000) @@ -586,86 +510,250 @@ struct bnx2x { #define CHIP_TIME(bp) ((CHIP_REV_IS_EMUL(bp)) ? 2000 : \ ((CHIP_REV_IS_FPGA(bp)) ? 200 : 1)) -#define CHIP_METAL(bp) (bp->chip_id & 0x00000ff0) -#define CHIP_BOND_ID(bp) (bp->chip_id & 0x0000000f) +#define CHIP_METAL(bp) (bp->common.chip_id & 0x00000ff0) +#define CHIP_BOND_ID(bp) (bp->common.chip_id & 0x0000000f) - u16 fw_seq; - u16 fw_drv_pulse_wr_seq; - u32 fw_mb; + int flash_size; +#define NVRAM_1MB_SIZE 0x20000 /* 1M bit in bytes */ +#define NVRAM_TIMEOUT_COUNT 30000 +#define NVRAM_PAGE_SIZE 256 - u32 hw_config; + u32 shmem_base; + + u32 hw_config; u32 board; - struct link_params link_params; + u32 bc_ver; + + char *name; +}; - struct link_vars link_vars; + +/* end of common */ + +/* port */ + +struct bnx2x_port { + u32 pmf; u32 link_config; - u32 supported; + u32 supported; +/* link settings - missing defines */ +#define SUPPORTED_2500baseX_Full (1 << 15) + + u32 advertising; /* link settings - missing defines */ -#define SUPPORTED_2500baseT_Full (1 << 15) +#define ADVERTISED_2500baseX_Full (1 << 15) - u32 phy_addr; + u32 phy_addr; /* used to synchronize phy accesses */ struct mutex phy_mutex; - u32 phy_id; + u32 port_stx; + struct nig_stats old_nig_stats; +}; - u32 advertising; -/* link settings - missing defines */ -#define ADVERTISED_2500baseT_Full (1 << 15) +/* end of port */ + +#define MAC_STX_NA 0xffffffff + +#ifdef BNX2X_MULTI +#define MAX_CONTEXT 16 +#else +#define MAX_CONTEXT 1 +#endif + +union cdu_context { + struct eth_context eth; + char pad[1024]; +}; + +#define MAX_DMAE_C 6 + +/* DMA memory not used in fastpath */ +struct bnx2x_slowpath { + union cdu_context context[MAX_CONTEXT]; + struct eth_stats_query fw_stats; + struct mac_configuration_cmd mac_config; + struct mac_configuration_cmd mcast_config; + + /* used by dmae command executer */ + struct dmae_command dmae[MAX_DMAE_C]; + + union mac_stats mac_stats; + struct nig_stats nig; + struct bnx2x_eth_stats eth_stats; + + u32 wb_comp; +#define BNX2X_WB_COMP_VAL 0xe0d0d0ae + u32 wb_data[4]; +}; + +#define bnx2x_sp(bp, var) (&bp->slowpath->var) +#define bnx2x_sp_mapping(bp, var) \ + (bp->slowpath_mapping + offsetof(struct bnx2x_slowpath, var)) + + +/* attn group wiring */ +#define MAX_DYNAMIC_ATTN_GRPS 8 + +struct attn_route { + u32 sig[4]; +}; + +struct bnx2x { + /* Fields used in the tx and intr/napi performance paths + * are grouped together in the beginning of the structure + */ + struct bnx2x_fastpath fp[MAX_CONTEXT]; + void __iomem *regview; + void __iomem *doorbells; +#define BNX2X_DB_SIZE (16*2048) + + struct net_device *dev; + struct pci_dev *pdev; + + atomic_t intr_sem; + struct msix_entry msix_table[MAX_CONTEXT+1]; + + int tx_ring_size; + +#ifdef BCM_VLAN + struct vlan_group *vlgrp; +#endif + u32 rx_csum; + u32 rx_offset; + u32 rx_buf_use_size; /* useable size */ + u32 rx_buf_size; /* with alignment */ +#define ETH_OVREHEAD (ETH_HLEN + 8) /* 8 for CRC + VLAN */ +#define ETH_MIN_PACKET_SIZE 60 +#define ETH_MAX_PACKET_SIZE 1500 +#define ETH_MAX_JUMBO_PACKET_SIZE 9600 - u32 bc_ver; + struct host_def_status_block *def_status_blk; +#define DEF_SB_ID 16 + u16 def_c_idx; + u16 def_u_idx; + u16 def_x_idx; + u16 def_t_idx; + u16 def_att_idx; + u32 attn_state; + struct attn_route attn_group[MAX_DYNAMIC_ATTN_GRPS]; + u32 aeu_mask; + u32 nig_mask; + + /* slow path ring */ + struct eth_spe *spq; + dma_addr_t spq_mapping; + u16 spq_prod_idx; + struct eth_spe *spq_prod_bd; + struct eth_spe *spq_last_bd; + u16 *dsb_sp_prod; + u16 spq_left; /* serialize spq */ + /* used to synchronize spq accesses */ + spinlock_t spq_lock; + + /* Flag for marking that there is either + * STAT_QUERY or CFC DELETE ramrod pending + */ + u8 stat_pending; + + /* End of fileds used in the performance code paths */ + + int panic; + int msglevel; + + u32 flags; +#define PCIX_FLAG 1 +#define PCI_32BIT_FLAG 2 +#define ONE_TDMA_FLAG 4 /* no longer used */ +#define NO_WOL_FLAG 8 +#define USING_DAC_FLAG 0x10 +#define USING_MSIX_FLAG 0x20 +#define ASF_ENABLE_FLAG 0x40 +#define NO_MCP_FLAG 0x100 +#define BP_NOMCP(bp) (bp->flags & NO_MCP_FLAG) + + int func; +#define BP_PORT(bp) (bp->func % PORT_MAX) +#define BP_FUNC(bp) (bp->func) +#define BP_E1HVN(bp) (bp->func >> 1) +#define BP_L_ID(bp) (BP_E1HVN(bp) << 2) +/* assorted E1HVN */ +#define IS_E1HMF(bp) (bp->e1hmf != 0) +#define BP_MAX_QUEUES(bp) (IS_E1HMF(bp) ? 4 : 16) + + int pm_cap; + int pcie_cap; + + struct work_struct sp_task; + struct work_struct reset_task; + + struct timer_list timer; + int timer_interval; + int current_interval; + + u16 fw_seq; + u16 fw_drv_pulse_wr_seq; + u32 func_stx; + + struct link_params link_params; + struct link_vars link_vars; - int flash_size; -#define NVRAM_1MB_SIZE 0x20000 /* 1M bit in bytes */ -#define NVRAM_TIMEOUT_COUNT 30000 -#define NVRAM_PAGE_SIZE 256 + struct bnx2x_common common; + struct bnx2x_port port; + + u32 mf_config; + u16 e1hov; + u8 e1hmf; u8 wol; - int rx_ring_size; + int rx_ring_size; - u16 tx_quick_cons_trip_int; - u16 tx_quick_cons_trip; - u16 tx_ticks_int; - u16 tx_ticks; + u16 tx_quick_cons_trip_int; + u16 tx_quick_cons_trip; + u16 tx_ticks_int; + u16 tx_ticks; - u16 rx_quick_cons_trip_int; - u16 rx_quick_cons_trip; - u16 rx_ticks_int; - u16 rx_ticks; + u16 rx_quick_cons_trip_int; + u16 rx_quick_cons_trip; + u16 rx_ticks_int; + u16 rx_ticks; - u32 stats_ticks; + u32 stats_ticks; + u32 lin_cnt; - int state; -#define BNX2X_STATE_CLOSED 0x0 -#define BNX2X_STATE_OPENING_WAIT4_LOAD 0x1000 -#define BNX2X_STATE_OPENING_WAIT4_PORT 0x2000 + int state; +#define BNX2X_STATE_CLOSED 0x0 +#define BNX2X_STATE_OPENING_WAIT4_LOAD 0x1000 +#define BNX2X_STATE_OPENING_WAIT4_PORT 0x2000 #define BNX2X_STATE_OPEN 0x3000 -#define BNX2X_STATE_CLOSING_WAIT4_HALT 0x4000 +#define BNX2X_STATE_CLOSING_WAIT4_HALT 0x4000 #define BNX2X_STATE_CLOSING_WAIT4_DELETE 0x5000 #define BNX2X_STATE_CLOSING_WAIT4_UNLOAD 0x6000 -#define BNX2X_STATE_ERROR 0xF000 +#define BNX2X_STATE_DISABLED 0xd000 +#define BNX2X_STATE_DIAG 0xe000 +#define BNX2X_STATE_ERROR 0xf000 - int num_queues; + int num_queues; - u32 rx_mode; -#define BNX2X_RX_MODE_NONE 0 -#define BNX2X_RX_MODE_NORMAL 1 -#define BNX2X_RX_MODE_ALLMULTI 2 -#define BNX2X_RX_MODE_PROMISC 3 -#define BNX2X_MAX_MULTICAST 64 -#define BNX2X_MAX_EMUL_MULTI 16 + u32 rx_mode; +#define BNX2X_RX_MODE_NONE 0 +#define BNX2X_RX_MODE_NORMAL 1 +#define BNX2X_RX_MODE_ALLMULTI 2 +#define BNX2X_RX_MODE_PROMISC 3 +#define BNX2X_MAX_MULTICAST 64 +#define BNX2X_MAX_EMUL_MULTI 16 - dma_addr_t def_status_blk_mapping; + dma_addr_t def_status_blk_mapping; - struct bnx2x_slowpath *slowpath; - dma_addr_t slowpath_mapping; + struct bnx2x_slowpath *slowpath; + dma_addr_t slowpath_mapping; #ifdef BCM_ISCSI void *t1; @@ -742,8 +830,10 @@ int bnx2x_set_gpio(struct bnx2x *bp, int gpio_num, u32 mode); /* MC hsi */ #define RX_COPY_THRESH 92 -#define BCM_PAGE_BITS 12 -#define BCM_PAGE_SIZE (1 << BCM_PAGE_BITS) +#define BCM_PAGE_SHIFT 12 +#define BCM_PAGE_SIZE (1 << BCM_PAGE_SHIFT) +#define BCM_PAGE_MASK (~(BCM_PAGE_SIZE - 1)) +#define BCM_PAGE_ALIGN(addr) (((addr) + BCM_PAGE_SIZE - 1) & BCM_PAGE_MASK) #define NUM_TX_RINGS 16 #define TX_DESC_CNT (BCM_PAGE_SIZE / sizeof(struct eth_tx_bd)) @@ -795,26 +885,11 @@ int bnx2x_set_gpio(struct bnx2x *bp, int gpio_num, u32 mode); /* must be used on a CID before placing it on a HW ring */ -#define HW_CID(bp, x) (x | (bp->port << 23)) +#define HW_CID(bp, x) ((BP_PORT(bp) << 23) | (BP_E1HVN(bp) << 17) | x) #define SP_DESC_CNT (BCM_PAGE_SIZE / sizeof(struct eth_spe)) #define MAX_SP_DESC_CNT (SP_DESC_CNT - 1) -#define ATTN_NIG_FOR_FUNC (1L << 8) -#define ATTN_SW_TIMER_4_FUNC (1L << 9) -#define GPIO_2_FUNC (1L << 10) -#define GPIO_3_FUNC (1L << 11) -#define GPIO_4_FUNC (1L << 12) -#define ATTN_GENERAL_ATTN_1 (1L << 13) -#define ATTN_GENERAL_ATTN_2 (1L << 14) -#define ATTN_GENERAL_ATTN_3 (1L << 15) -#define ATTN_GENERAL_ATTN_4 (1L << 13) -#define ATTN_GENERAL_ATTN_5 (1L << 14) -#define ATTN_GENERAL_ATTN_6 (1L << 15) - -#define ATTN_HARD_WIRED_MASK 0xff00 -#define ATTENTION_ID 4 - #define BNX2X_BTR 3 #define MAX_SPQ_PENDING 8 @@ -831,6 +906,31 @@ int bnx2x_set_gpio(struct bnx2x *bp, int gpio_num, u32 mode); DPM_TRIGER_TYPE); \ } while (0) +static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms, + int wait) +{ + u32 val; + + do { + val = REG_RD(bp, reg); + if (val == expected) + break; + ms -= wait; + msleep(wait); + + } while (ms > 0); + + return val; +} + + +/* load/unload mode */ +#define LOAD_NORMAL 0 +#define LOAD_OPEN 1 +#define LOAD_DIAG 2 +#define UNLOAD_NORMAL 0 +#define UNLOAD_CLOSE 1 + /* DMAE command defines */ #define DMAE_CMD_SRC_PCI 0 #define DMAE_CMD_SRC_GRC DMAE_COMMAND_SRC @@ -877,23 +977,48 @@ int bnx2x_set_gpio(struct bnx2x *bp, int gpio_num, u32 mode); #define pbd_tcp_flags(skb) (ntohl(tcp_flag_word(tcp_hdr(skb)))>>16 & 0xff) -/* stuff added to make the code fit 80Col */ +/* must be used on a CID before placing it on a HW ring */ -#define TPA_TYPE_START ETH_FAST_PATH_RX_CQE_START_FLG -#define TPA_TYPE_END ETH_FAST_PATH_RX_CQE_END_FLG -#define TPA_TYPE(cqe) (cqe->fast_path_cqe.error_type_flags & \ - (TPA_TYPE_START | TPA_TYPE_END)) #define BNX2X_RX_SUM_OK(cqe) \ (!(cqe->fast_path_cqe.status_flags & \ (ETH_FAST_PATH_RX_CQE_IP_XSUM_NO_VALIDATION_FLG | \ ETH_FAST_PATH_RX_CQE_L4_XSUM_NO_VALIDATION_FLG))) -#define BNX2X_RX_SUM_FIX(cqe) \ - ((le16_to_cpu(cqe->fast_path_cqe.pars_flags.flags) & \ - PARSING_FLAGS_OVER_ETHERNET_PROTOCOL) == \ - (1 << PARSING_FLAGS_OVER_ETHERNET_PROTOCOL_SHIFT)) +/* CMNG constants + derived from lab experiments, and not from system spec calculations !!! */ +#define DEF_MIN_RATE 100 +/* resolution of the rate shaping timer - 100 usec */ +#define RS_PERIODIC_TIMEOUT_USEC 100 +/* resolution of fairness algorithm in usecs - + coefficient for clauclating the actuall t fair */ +#define T_FAIR_COEF 10000000 +/* number of bytes in single QM arbitration cycle - + coeffiecnt for calculating the fairness timer */ +#define QM_ARB_BYTES 40000 +#define FAIR_MEM 2 + + +#define ATTN_NIG_FOR_FUNC (1L << 8) +#define ATTN_SW_TIMER_4_FUNC (1L << 9) +#define GPIO_2_FUNC (1L << 10) +#define GPIO_3_FUNC (1L << 11) +#define GPIO_4_FUNC (1L << 12) +#define ATTN_GENERAL_ATTN_1 (1L << 13) +#define ATTN_GENERAL_ATTN_2 (1L << 14) +#define ATTN_GENERAL_ATTN_3 (1L << 15) +#define ATTN_GENERAL_ATTN_4 (1L << 13) +#define ATTN_GENERAL_ATTN_5 (1L << 14) +#define ATTN_GENERAL_ATTN_6 (1L << 15) + +#define ATTN_HARD_WIRED_MASK 0xff00 +#define ATTENTION_ID 4 +/* stuff added to make the code fit 80Col */ + +#define BNX2X_PMF_LINK_ASSERT \ + GENERAL_ATTEN_OFFSET(LINK_SYNC_ATTENTION_BIT_FUNC_0 + BP_FUNC(bp)) + #define BNX2X_MC_ASSERT_BITS \ (GENERAL_ATTEN_OFFSET(TSTORM_FATAL_ASSERT_ATTENTION_BIT) | \ GENERAL_ATTEN_OFFSET(USTORM_FATAL_ASSERT_ATTENTION_BIT) | \ @@ -906,12 +1031,20 @@ int bnx2x_set_gpio(struct bnx2x *bp, int gpio_num, u32 mode); #define BNX2X_DOORQ_ASSERT \ AEU_INPUTS_ATTN_BITS_DOORBELLQ_HW_INTERRUPT +#define BNX2X_GRC_TIMEOUT GENERAL_ATTEN_OFFSET(LATCHED_ATTN_TIMEOUT_GRC) +#define BNX2X_GRC_RSV (GENERAL_ATTEN_OFFSET(LATCHED_ATTN_RBCR) | \ + GENERAL_ATTEN_OFFSET(LATCHED_ATTN_RBCT) | \ + GENERAL_ATTEN_OFFSET(LATCHED_ATTN_RBCN) | \ + GENERAL_ATTEN_OFFSET(LATCHED_ATTN_RBCU) | \ + GENERAL_ATTEN_OFFSET(LATCHED_ATTN_RBCP) | \ + GENERAL_ATTEN_OFFSET(LATCHED_ATTN_RSVD_GRC)) + #define HW_INTERRUT_ASSERT_SET_0 \ (AEU_INPUTS_ATTN_BITS_TSDM_HW_INTERRUPT | \ AEU_INPUTS_ATTN_BITS_TCM_HW_INTERRUPT | \ AEU_INPUTS_ATTN_BITS_TSEMI_HW_INTERRUPT | \ AEU_INPUTS_ATTN_BITS_PBF_HW_INTERRUPT) -#define HW_PRTY_ASSERT_SET_0 (AEU_INPUTS_ATTN_BITS_BRB_PARITY_ERROR | \ +#define HW_PRTY_ASSERT_SET_0 (AEU_INPUTS_ATTN_BITS_BRB_PARITY_ERROR | \ AEU_INPUTS_ATTN_BITS_PARSER_PARITY_ERROR | \ AEU_INPUTS_ATTN_BITS_TSDM_PARITY_ERROR | \ AEU_INPUTS_ATTN_BITS_SEARCHER_PARITY_ERROR |\ @@ -928,7 +1061,7 @@ int bnx2x_set_gpio(struct bnx2x *bp, int gpio_num, u32 mode); AEU_INPUTS_ATTN_BITS_UPB_HW_INTERRUPT | \ AEU_INPUTS_ATTN_BITS_CSDM_HW_INTERRUPT | \ AEU_INPUTS_ATTN_BITS_CCM_HW_INTERRUPT) -#define HW_PRTY_ASSERT_SET_1 (AEU_INPUTS_ATTN_BITS_PBCLIENT_PARITY_ERROR |\ +#define HW_PRTY_ASSERT_SET_1 (AEU_INPUTS_ATTN_BITS_PBCLIENT_PARITY_ERROR |\ AEU_INPUTS_ATTN_BITS_QM_PARITY_ERROR | \ AEU_INPUTS_ATTN_BITS_XSDM_PARITY_ERROR | \ AEU_INPUTS_ATTN_BITS_XSEMI_PARITY_ERROR | \ @@ -945,7 +1078,7 @@ int bnx2x_set_gpio(struct bnx2x *bp, int gpio_num, u32 mode); AEU_INPUTS_ATTN_BITS_DMAE_HW_INTERRUPT | \ AEU_INPUTS_ATTN_BITS_PXPPCICLOCKCLIENT_HW_INTERRUPT |\ AEU_INPUTS_ATTN_BITS_MISC_HW_INTERRUPT) -#define HW_PRTY_ASSERT_SET_2 (AEU_INPUTS_ATTN_BITS_CSEMI_PARITY_ERROR | \ +#define HW_PRTY_ASSERT_SET_2 (AEU_INPUTS_ATTN_BITS_CSEMI_PARITY_ERROR | \ AEU_INPUTS_ATTN_BITS_PXP_PARITY_ERROR | \ AEU_INPUTS_ATTN_BITS_PXPPCICLOCKCLIENT_PARITY_ERROR |\ AEU_INPUTS_ATTN_BITS_CFC_PARITY_ERROR | \ @@ -954,42 +1087,44 @@ int bnx2x_set_gpio(struct bnx2x *bp, int gpio_num, u32 mode); AEU_INPUTS_ATTN_BITS_MISC_PARITY_ERROR) -#define ETH_RX_ERROR_FALGS (ETH_FAST_PATH_RX_CQE_PHY_DECODE_ERR_FLG | \ - ETH_FAST_PATH_RX_CQE_IP_BAD_XSUM_FLG | \ - ETH_FAST_PATH_RX_CQE_L4_BAD_XSUM_FLG) - - #define MULTI_FLAGS \ - (TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_CAPABILITY | \ - TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_TCP_CAPABILITY | \ - TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV6_CAPABILITY | \ - TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV6_TCP_CAPABILITY | \ - TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_ENABLE) + (TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_CAPABILITY | \ + TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_TCP_CAPABILITY | \ + TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV6_CAPABILITY | \ + TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV6_TCP_CAPABILITY | \ + TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_ENABLE) -#define MULTI_MASK 0x7f +#define MULTI_MASK 0x7f -#define U_SB_ETH_RX_CQ_INDEX HC_INDEX_U_ETH_RX_CQ_CONS -#define C_SB_ETH_TX_CQ_INDEX HC_INDEX_C_ETH_TX_CQ_CONS -#define C_DEF_SB_SP_INDEX HC_INDEX_DEF_C_ETH_SLOW_PATH - -#define BNX2X_RX_SB_INDEX \ - &fp->status_blk->u_status_block.index_values[U_SB_ETH_RX_CQ_INDEX] +#define DEF_USB_FUNC_OFF (2 + 2*HC_USTORM_DEF_SB_NUM_INDICES) +#define DEF_CSB_FUNC_OFF (2 + 2*HC_CSTORM_DEF_SB_NUM_INDICES) +#define DEF_XSB_FUNC_OFF (2 + 2*HC_XSTORM_DEF_SB_NUM_INDICES) +#define DEF_TSB_FUNC_OFF (2 + 2*HC_TSTORM_DEF_SB_NUM_INDICES) -#define BNX2X_TX_SB_INDEX \ - &fp->status_blk->c_status_block.index_values[C_SB_ETH_TX_CQ_INDEX] +#define C_DEF_SB_SP_INDEX HC_INDEX_DEF_C_ETH_SLOW_PATH #define BNX2X_SP_DSB_INDEX \ -&bp->def_status_blk->c_def_status_block.index_values[C_DEF_SB_SP_INDEX] +(&bp->def_status_blk->c_def_status_block.index_values[C_DEF_SB_SP_INDEX]) #define CAM_IS_INVALID(x) \ (x.target_table_entry.flags == TSTORM_CAM_TARGET_TABLE_ENTRY_ACTION_TYPE) #define CAM_INVALIDATE(x) \ -x.target_table_entry.flags = TSTORM_CAM_TARGET_TABLE_ENTRY_ACTION_TYPE + (x.target_table_entry.flags = TSTORM_CAM_TARGET_TABLE_ENTRY_ACTION_TYPE) + + +/* Number of u32 elements in MC hash array */ +#define MC_HASH_SIZE 8 +#define MC_HASH_OFFSET(bp, i) (BAR_TSTRORM_INTMEM + \ + TSTORM_APPROXIMATE_MATCH_MULTICAST_FILTERING_OFFSET(BP_FUNC(bp)) + i*4) +#ifndef PXP2_REG_PXP2_INT_STS +#define PXP2_REG_PXP2_INT_STS PXP2_REG_PXP2_INT_STS_0 +#endif + /* MISC_REG_RESET_REG - this is here for the hsi to work don't touch */ #endif /* bnx2x.h */ diff --git a/drivers/net/bnx2x_fw_defs.h b/drivers/net/bnx2x_fw_defs.h index 3b968904ca65..e3da7f69d27b 100644 --- a/drivers/net/bnx2x_fw_defs.h +++ b/drivers/net/bnx2x_fw_defs.h @@ -8,191 +8,390 @@ */ -#define CSTORM_DEF_SB_HC_DISABLE_OFFSET(port, index)\ - (0x1922 + (port * 0x40) + (index * 0x4)) -#define CSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(port)\ - (0x1900 + (port * 0x40)) -#define CSTORM_HC_BTR_OFFSET(port)\ - (0x1984 + (port * 0xc0)) -#define CSTORM_SB_HC_DISABLE_OFFSET(port, cpu_id, index)\ - (0x141a + (port * 0x280) + (cpu_id * 0x28) + (index * 0x4)) -#define CSTORM_SB_HC_TIMEOUT_OFFSET(port, cpu_id, index)\ - (0x1418 + (port * 0x280) + (cpu_id * 0x28) + (index * 0x4)) -#define CSTORM_SB_HOST_SB_ADDR_OFFSET(port, cpu_id)\ - (0x1400 + (port * 0x280) + (cpu_id * 0x28)) -#define CSTORM_STATS_FLAGS_OFFSET(port) (0x5108 + (port * 0x8)) -#define TSTORM_CLIENT_CONFIG_OFFSET(port, client_id)\ - (0x1510 + (port * 0x240) + (client_id * 0x20)) -#define TSTORM_DEF_SB_HC_DISABLE_OFFSET(port, index)\ - (0x138a + (port * 0x28) + (index * 0x4)) -#define TSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(port)\ - (0x1370 + (port * 0x28)) -#define TSTORM_ETH_STATS_QUERY_ADDR_OFFSET(port)\ - (0x4b70 + (port * 0x8)) -#define TSTORM_FUNCTION_COMMON_CONFIG_OFFSET(function)\ - (0x1418 + (function * 0x30)) -#define TSTORM_HC_BTR_OFFSET(port)\ - (0x13c4 + (port * 0x18)) -#define TSTORM_INDIRECTION_TABLE_OFFSET(port)\ - (0x22c8 + (port * 0x80)) -#define TSTORM_INDIRECTION_TABLE_SIZE 0x80 -#define TSTORM_MAC_FILTER_CONFIG_OFFSET(port)\ - (0x1420 + (port * 0x30)) -#define TSTORM_RCQ_PROD_OFFSET(port, client_id)\ - (0x1508 + (port * 0x240) + (client_id * 0x20)) -#define TSTORM_STATS_FLAGS_OFFSET(port) (0x4b90 + (port * 0x8)) -#define USTORM_DEF_SB_HC_DISABLE_OFFSET(port, index)\ - (0x191a + (port * 0x28) + (index * 0x4)) -#define USTORM_DEF_SB_HOST_SB_ADDR_OFFSET(port)\ - (0x1900 + (port * 0x28)) -#define USTORM_HC_BTR_OFFSET(port)\ - (0x1954 + (port * 0xb8)) -#define USTORM_MEM_WORKAROUND_ADDRESS_OFFSET(port)\ - (0x5408 + (port * 0x8)) -#define USTORM_SB_HC_DISABLE_OFFSET(port, cpu_id, index)\ - (0x141a + (port * 0x280) + (cpu_id * 0x28) + (index * 0x4)) -#define USTORM_SB_HC_TIMEOUT_OFFSET(port, cpu_id, index)\ - (0x1418 + (port * 0x280) + (cpu_id * 0x28) + (index * 0x4)) -#define USTORM_SB_HOST_SB_ADDR_OFFSET(port, cpu_id)\ - (0x1400 + (port * 0x280) + (cpu_id * 0x28)) -#define XSTORM_ASSERT_LIST_INDEX_OFFSET 0x1000 -#define XSTORM_ASSERT_LIST_OFFSET(idx) (0x1020 + (idx * 0x10)) -#define XSTORM_DEF_SB_HC_DISABLE_OFFSET(port, index)\ - (0x141a + (port * 0x28) + (index * 0x4)) -#define XSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(port)\ - (0x1400 + (port * 0x28)) -#define XSTORM_ETH_STATS_QUERY_ADDR_OFFSET(port)\ - (0x5408 + (port * 0x8)) -#define XSTORM_HC_BTR_OFFSET(port)\ - (0x1454 + (port * 0x18)) -#define XSTORM_SPQ_PAGE_BASE_OFFSET(port)\ - (0x5328 + (port * 0x18)) -#define XSTORM_SPQ_PROD_OFFSET(port)\ - (0x5330 + (port * 0x18)) -#define XSTORM_STATS_FLAGS_OFFSET(port) (0x53f8 + (port * 0x8)) +#define CSTORM_ASSERT_LIST_INDEX_OFFSET \ + (IS_E1H_OFFSET? 0x7000 : 0x1000) +#define CSTORM_ASSERT_LIST_OFFSET(idx) \ + (IS_E1H_OFFSET? (0x7020 + (idx * 0x10)) : (0x1020 + (idx * 0x10))) +#define CSTORM_DEF_SB_HC_DISABLE_OFFSET(function, index) \ + (IS_E1H_OFFSET? (0x8522 + ((function>>1) * 0x40) + ((function&1) \ + * 0x100) + (index * 0x4)) : (0x1922 + (function * 0x40) + (index \ + * 0x4))) +#define CSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(function) \ + (IS_E1H_OFFSET? (0x8500 + ((function>>1) * 0x40) + ((function&1) \ + * 0x100)) : (0x1900 + (function * 0x40))) +#define CSTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(function) \ + (IS_E1H_OFFSET? (0x8508 + ((function>>1) * 0x40) + ((function&1) \ + * 0x100)) : (0x1908 + (function * 0x40))) +#define CSTORM_FUNCTION_MODE_OFFSET \ + (IS_E1H_OFFSET? 0x11e8 : 0xffffffff) +#define CSTORM_HC_BTR_OFFSET(port) \ + (IS_E1H_OFFSET? (0x8704 + (port * 0xf0)) : (0x1984 + (port * 0xc0))) +#define CSTORM_SB_HC_DISABLE_OFFSET(port, cpu_id, index) \ + (IS_E1H_OFFSET? (0x801a + (port * 0x280) + (cpu_id * 0x28) + \ + (index * 0x4)) : (0x141a + (port * 0x280) + (cpu_id * 0x28) + \ + (index * 0x4))) +#define CSTORM_SB_HC_TIMEOUT_OFFSET(port, cpu_id, index) \ + (IS_E1H_OFFSET? (0x8018 + (port * 0x280) + (cpu_id * 0x28) + \ + (index * 0x4)) : (0x1418 + (port * 0x280) + (cpu_id * 0x28) + \ + (index * 0x4))) +#define CSTORM_SB_HOST_SB_ADDR_OFFSET(port, cpu_id) \ + (IS_E1H_OFFSET? (0x8000 + (port * 0x280) + (cpu_id * 0x28)) : \ + (0x1400 + (port * 0x280) + (cpu_id * 0x28))) +#define CSTORM_SB_HOST_STATUS_BLOCK_OFFSET(port, cpu_id) \ + (IS_E1H_OFFSET? (0x8008 + (port * 0x280) + (cpu_id * 0x28)) : \ + (0x1408 + (port * 0x280) + (cpu_id * 0x28))) +#define CSTORM_STATS_FLAGS_OFFSET(function) \ + (IS_E1H_OFFSET? (0x1108 + (function * 0x8)) : (0x5108 + \ + (function * 0x8))) +#define TSTORM_APPROXIMATE_MATCH_MULTICAST_FILTERING_OFFSET(function) \ + (IS_E1H_OFFSET? (0x31c0 + (function * 0x20)) : 0xffffffff) +#define TSTORM_ASSERT_LIST_INDEX_OFFSET \ + (IS_E1H_OFFSET? 0xa000 : 0x1000) +#define TSTORM_ASSERT_LIST_OFFSET(idx) \ + (IS_E1H_OFFSET? (0xa020 + (idx * 0x10)) : (0x1020 + (idx * 0x10))) +#define TSTORM_CLIENT_CONFIG_OFFSET(port, client_id) \ + (IS_E1H_OFFSET? (0x3358 + (port * 0x3e8) + (client_id * 0x28)) : \ + (0x9c8 + (port * 0x2f8) + (client_id * 0x28))) +#define TSTORM_DEF_SB_HC_DISABLE_OFFSET(function, index) \ + (IS_E1H_OFFSET? (0xb01a + ((function>>1) * 0x28) + ((function&1) \ + * 0xa0) + (index * 0x4)) : (0x141a + (function * 0x28) + (index * \ + 0x4))) +#define TSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(function) \ + (IS_E1H_OFFSET? (0xb000 + ((function>>1) * 0x28) + ((function&1) \ + * 0xa0)) : (0x1400 + (function * 0x28))) +#define TSTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(function) \ + (IS_E1H_OFFSET? (0xb008 + ((function>>1) * 0x28) + ((function&1) \ + * 0xa0)) : (0x1408 + (function * 0x28))) +#define TSTORM_ETH_STATS_QUERY_ADDR_OFFSET(function) \ + (IS_E1H_OFFSET? (0x2b80 + (function * 0x8)) : (0x4b68 + \ + (function * 0x8))) +#define TSTORM_FUNCTION_COMMON_CONFIG_OFFSET(function) \ + (IS_E1H_OFFSET? (0x3000 + (function * 0x38)) : (0x1500 + \ + (function * 0x38))) +#define TSTORM_FUNCTION_MODE_OFFSET \ + (IS_E1H_OFFSET? 0x1ad0 : 0xffffffff) +#define TSTORM_HC_BTR_OFFSET(port) \ + (IS_E1H_OFFSET? (0xb144 + (port * 0x30)) : (0x1454 + (port * 0x18))) +#define TSTORM_INDIRECTION_TABLE_OFFSET(function) \ + (IS_E1H_OFFSET? (0x12c8 + (function * 0x80)) : (0x22c8 + \ + (function * 0x80))) +#define TSTORM_INDIRECTION_TABLE_SIZE 0x80 +#define TSTORM_MAC_FILTER_CONFIG_OFFSET(function) \ + (IS_E1H_OFFSET? (0x3008 + (function * 0x38)) : (0x1508 + \ + (function * 0x38))) +#define TSTORM_RX_PRODS_OFFSET(port, client_id) \ + (IS_E1H_OFFSET? (0x3350 + (port * 0x3e8) + (client_id * 0x28)) : \ + (0x9c0 + (port * 0x2f8) + (client_id * 0x28))) +#define TSTORM_STATS_FLAGS_OFFSET(function) \ + (IS_E1H_OFFSET? (0x2c00 + (function * 0x8)) : (0x4b88 + \ + (function * 0x8))) +#define TSTORM_TPA_EXIST_OFFSET (IS_E1H_OFFSET? 0x3b30 : 0x1c20) +#define USTORM_AGG_DATA_OFFSET (IS_E1H_OFFSET? 0xa040 : 0x2c10) +#define USTORM_AGG_DATA_SIZE (IS_E1H_OFFSET? 0x2440 : 0x1200) +#define USTORM_ASSERT_LIST_INDEX_OFFSET \ + (IS_E1H_OFFSET? 0x8000 : 0x1000) +#define USTORM_ASSERT_LIST_OFFSET(idx) \ + (IS_E1H_OFFSET? (0x8020 + (idx * 0x10)) : (0x1020 + (idx * 0x10))) +#define USTORM_CQE_PAGE_BASE_OFFSET(port, clientId) \ + (IS_E1H_OFFSET? (0x3298 + (port * 0x258) + (clientId * 0x18)) : \ + (0x5450 + (port * 0x1c8) + (clientId * 0x18))) +#define USTORM_DEF_SB_HC_DISABLE_OFFSET(function, index) \ + (IS_E1H_OFFSET? (0x951a + ((function>>1) * 0x28) + ((function&1) \ + * 0xa0) + (index * 0x4)) : (0x191a + (function * 0x28) + (index * \ + 0x4))) +#define USTORM_DEF_SB_HOST_SB_ADDR_OFFSET(function) \ + (IS_E1H_OFFSET? (0x9500 + ((function>>1) * 0x28) + ((function&1) \ + * 0xa0)) : (0x1900 + (function * 0x28))) +#define USTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(function) \ + (IS_E1H_OFFSET? (0x9508 + ((function>>1) * 0x28) + ((function&1) \ + * 0xa0)) : (0x1908 + (function * 0x28))) +#define USTORM_FUNCTION_MODE_OFFSET \ + (IS_E1H_OFFSET? 0x2448 : 0xffffffff) +#define USTORM_HC_BTR_OFFSET(port) \ + (IS_E1H_OFFSET? (0x9644 + (port * 0xd0)) : (0x1954 + (port * 0xb8))) +#define USTORM_MAX_AGG_SIZE_OFFSET(port, clientId) \ + (IS_E1H_OFFSET? (0x3290 + (port * 0x258) + (clientId * 0x18)) : \ + (0x5448 + (port * 0x1c8) + (clientId * 0x18))) +#define USTORM_MEM_WORKAROUND_ADDRESS_OFFSET(function) \ + (IS_E1H_OFFSET? (0x2408 + (function * 0x8)) : (0x5408 + \ + (function * 0x8))) +#define USTORM_SB_HC_DISABLE_OFFSET(port, cpu_id, index) \ + (IS_E1H_OFFSET? (0x901a + (port * 0x280) + (cpu_id * 0x28) + \ + (index * 0x4)) : (0x141a + (port * 0x280) + (cpu_id * 0x28) + \ + (index * 0x4))) +#define USTORM_SB_HC_TIMEOUT_OFFSET(port, cpu_id, index) \ + (IS_E1H_OFFSET? (0x9018 + (port * 0x280) + (cpu_id * 0x28) + \ + (index * 0x4)) : (0x1418 + (port * 0x280) + (cpu_id * 0x28) + \ + (index * 0x4))) +#define USTORM_SB_HOST_SB_ADDR_OFFSET(port, cpu_id) \ + (IS_E1H_OFFSET? (0x9000 + (port * 0x280) + (cpu_id * 0x28)) : \ + (0x1400 + (port * 0x280) + (cpu_id * 0x28))) +#define USTORM_SB_HOST_STATUS_BLOCK_OFFSET(port, cpu_id) \ + (IS_E1H_OFFSET? (0x9008 + (port * 0x280) + (cpu_id * 0x28)) : \ + (0x1408 + (port * 0x280) + (cpu_id * 0x28))) +#define XSTORM_ASSERT_LIST_INDEX_OFFSET \ + (IS_E1H_OFFSET? 0x9000 : 0x1000) +#define XSTORM_ASSERT_LIST_OFFSET(idx) \ + (IS_E1H_OFFSET? (0x9020 + (idx * 0x10)) : (0x1020 + (idx * 0x10))) +#define XSTORM_CMNG_PER_PORT_VARS_OFFSET(port) \ + (IS_E1H_OFFSET? (0x24a8 + (port * 0x40)) : (0x3ba0 + (port * 0x40))) +#define XSTORM_DEF_SB_HC_DISABLE_OFFSET(function, index) \ + (IS_E1H_OFFSET? (0xa01a + ((function>>1) * 0x28) + ((function&1) \ + * 0xa0) + (index * 0x4)) : (0x141a + (function * 0x28) + (index * \ + 0x4))) +#define XSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(function) \ + (IS_E1H_OFFSET? (0xa000 + ((function>>1) * 0x28) + ((function&1) \ + * 0xa0)) : (0x1400 + (function * 0x28))) +#define XSTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(function) \ + (IS_E1H_OFFSET? (0xa008 + ((function>>1) * 0x28) + ((function&1) \ + * 0xa0)) : (0x1408 + (function * 0x28))) +#define XSTORM_E1HOV_OFFSET(function) \ + (IS_E1H_OFFSET? (0x2ab8 + (function * 0x2)) : 0xffffffff) +#define XSTORM_ETH_STATS_QUERY_ADDR_OFFSET(function) \ + (IS_E1H_OFFSET? (0x2418 + (function * 0x8)) : (0x3b70 + \ + (function * 0x8))) +#define XSTORM_FAIRNESS_PER_VN_VARS_OFFSET(function) \ + (IS_E1H_OFFSET? (0x2568 + (function * 0x70)) : (0x3c60 + \ + (function * 0x70))) +#define XSTORM_FUNCTION_MODE_OFFSET \ + (IS_E1H_OFFSET? 0x2ac8 : 0xffffffff) +#define XSTORM_HC_BTR_OFFSET(port) \ + (IS_E1H_OFFSET? (0xa144 + (port * 0x30)) : (0x1454 + (port * 0x18))) +#define XSTORM_RATE_SHAPING_PER_VN_VARS_OFFSET(function) \ + (IS_E1H_OFFSET? (0x2528 + (function * 0x70)) : (0x3c20 + \ + (function * 0x70))) +#define XSTORM_SPQ_PAGE_BASE_OFFSET(function) \ + (IS_E1H_OFFSET? (0x2000 + (function * 0x10)) : (0x3328 + \ + (function * 0x10))) +#define XSTORM_SPQ_PROD_OFFSET(function) \ + (IS_E1H_OFFSET? (0x2008 + (function * 0x10)) : (0x3330 + \ + (function * 0x10))) +#define XSTORM_STATS_FLAGS_OFFSET(function) \ + (IS_E1H_OFFSET? (0x23d8 + (function * 0x8)) : (0x3b60 + \ + (function * 0x8))) #define COMMON_ASM_INVALID_ASSERT_OPCODE 0x0 /** * This file defines HSI constatnts for the ETH flow */ - -/* hash types */ -#define DEFAULT_HASH_TYPE 0 -#define IPV4_HASH_TYPE 1 -#define TCP_IPV4_HASH_TYPE 2 -#define IPV6_HASH_TYPE 3 -#define TCP_IPV6_HASH_TYPE 4 +#ifdef _EVEREST_MICROCODE +#include "microcode_constants.h" +#include "eth_rx_bd.h" +#include "eth_tx_bd.h" +#include "eth_rx_cqe.h" +#include "eth_rx_sge.h" +#include "eth_rx_cqe_next_page.h" +#endif + +/* RSS hash types */ +#define DEFAULT_HASH_TYPE 0 +#define IPV4_HASH_TYPE 1 +#define TCP_IPV4_HASH_TYPE 2 +#define IPV6_HASH_TYPE 3 +#define TCP_IPV6_HASH_TYPE 4 + +/* Ethernet Ring parmaters */ +#define X_ETH_LOCAL_RING_SIZE 13 +#define FIRST_BD_IN_PKT 0 +#define PARSE_BD_INDEX 1 +#define NUM_OF_ETH_BDS_IN_PAGE \ + ((PAGE_SIZE) / (STRUCT_SIZE(eth_tx_bd)/8)) + + +/* Rx ring params */ +#define U_ETH_LOCAL_BD_RING_SIZE (16) +#define U_ETH_LOCAL_SGE_RING_SIZE (12) +#define U_ETH_SGL_SIZE (8) + + +#define U_ETH_BDS_PER_PAGE_MASK \ + ((PAGE_SIZE/(STRUCT_SIZE(eth_rx_bd)/8))-1) +#define U_ETH_CQE_PER_PAGE_MASK \ + ((PAGE_SIZE/(STRUCT_SIZE(eth_rx_cqe)/8))-1) +#define U_ETH_SGES_PER_PAGE_MASK \ + ((PAGE_SIZE/(STRUCT_SIZE(eth_rx_sge)/8))-1) + +#define U_ETH_SGES_PER_PAGE_INVERSE_MASK \ + (0xFFFF - ((PAGE_SIZE/((STRUCT_SIZE(eth_rx_sge))/8))-1)) + + +#define TU_ETH_CQES_PER_PAGE \ + (PAGE_SIZE/(STRUCT_SIZE(eth_rx_cqe_next_page)/8)) +#define U_ETH_BDS_PER_PAGE (PAGE_SIZE/(STRUCT_SIZE(eth_rx_bd)/8)) +#define U_ETH_SGES_PER_PAGE (PAGE_SIZE/(STRUCT_SIZE(eth_rx_sge)/8)) + +#define U_ETH_UNDEFINED_Q 0xFF /* values of command IDs in the ramrod message */ -#define RAMROD_CMD_ID_ETH_PORT_SETUP (80) -#define RAMROD_CMD_ID_ETH_CLIENT_SETUP (85) -#define RAMROD_CMD_ID_ETH_STAT_QUERY (90) -#define RAMROD_CMD_ID_ETH_UPDATE (100) -#define RAMROD_CMD_ID_ETH_HALT (105) -#define RAMROD_CMD_ID_ETH_SET_MAC (110) -#define RAMROD_CMD_ID_ETH_CFC_DEL (115) -#define RAMROD_CMD_ID_ETH_PORT_DEL (120) -#define RAMROD_CMD_ID_ETH_FORWARD_SETUP (125) +#define RAMROD_CMD_ID_ETH_PORT_SETUP (80) +#define RAMROD_CMD_ID_ETH_CLIENT_SETUP (85) +#define RAMROD_CMD_ID_ETH_STAT_QUERY (90) +#define RAMROD_CMD_ID_ETH_UPDATE (100) +#define RAMROD_CMD_ID_ETH_HALT (105) +#define RAMROD_CMD_ID_ETH_SET_MAC (110) +#define RAMROD_CMD_ID_ETH_CFC_DEL (115) +#define RAMROD_CMD_ID_ETH_PORT_DEL (120) +#define RAMROD_CMD_ID_ETH_FORWARD_SETUP (125) /* command values for set mac command */ -#define T_ETH_MAC_COMMAND_SET 0 -#define T_ETH_MAC_COMMAND_INVALIDATE 1 +#define T_ETH_MAC_COMMAND_SET 0 +#define T_ETH_MAC_COMMAND_INVALIDATE 1 + +#define T_ETH_INDIRECTION_TABLE_SIZE 128 -#define T_ETH_INDIRECTION_TABLE_SIZE 128 +/*The CRC32 seed, that is used for the hash(reduction) multicast address */ +#define T_ETH_CRC32_HASH_SEED 0x00000000 /* Maximal L2 clients supported */ -#define ETH_MAX_RX_CLIENTS (18) +#define ETH_MAX_RX_CLIENTS_E1 19 +#define ETH_MAX_RX_CLIENTS_E1H 25 + +/* Maximal aggregation queues supported */ +#define ETH_MAX_AGGREGATION_QUEUES_E1 (32) +#define ETH_MAX_AGGREGATION_QUEUES_E1H (64) + /** * This file defines HSI constatnts common to all microcode flows */ /* Connection types */ -#define ETH_CONNECTION_TYPE 0 +#define ETH_CONNECTION_TYPE 0 +#define TOE_CONNECTION_TYPE 1 +#define RDMA_CONNECTION_TYPE 2 +#define ISCSI_CONNECTION_TYPE 3 +#define FCOE_CONNECTION_TYPE 4 +#define RESERVED_CONNECTION_TYPE_0 5 +#define RESERVED_CONNECTION_TYPE_1 6 +#define RESERVED_CONNECTION_TYPE_2 7 + -#define PROTOCOL_STATE_BIT_OFFSET 6 +#define PROTOCOL_STATE_BIT_OFFSET 6 -#define ETH_STATE (ETH_CONNECTION_TYPE << PROTOCOL_STATE_BIT_OFFSET) +#define ETH_STATE (ETH_CONNECTION_TYPE << PROTOCOL_STATE_BIT_OFFSET) +#define TOE_STATE (TOE_CONNECTION_TYPE << PROTOCOL_STATE_BIT_OFFSET) +#define RDMA_STATE (RDMA_CONNECTION_TYPE << PROTOCOL_STATE_BIT_OFFSET) +#define ISCSI_STATE \ + (ISCSI_CONNECTION_TYPE << PROTOCOL_STATE_BIT_OFFSET) +#define FCOE_STATE (FCOE_CONNECTION_TYPE << PROTOCOL_STATE_BIT_OFFSET) /* microcode fixed page page size 4K (chains and ring segments) */ -#define MC_PAGE_SIZE (4096) +#define MC_PAGE_SIZE (4096) -/* Host coalescing constants */ -/* IGU constants */ -#define IGU_PORT_BASE 0x0400 - -#define IGU_ADDR_MSIX 0x0000 -#define IGU_ADDR_INT_ACK 0x0200 -#define IGU_ADDR_PROD_UPD 0x0201 -#define IGU_ADDR_ATTN_BITS_UPD 0x0202 -#define IGU_ADDR_ATTN_BITS_SET 0x0203 -#define IGU_ADDR_ATTN_BITS_CLR 0x0204 -#define IGU_ADDR_COALESCE_NOW 0x0205 -#define IGU_ADDR_SIMD_MASK 0x0206 -#define IGU_ADDR_SIMD_NOMASK 0x0207 -#define IGU_ADDR_MSI_CTL 0x0210 -#define IGU_ADDR_MSI_ADDR_LO 0x0211 -#define IGU_ADDR_MSI_ADDR_HI 0x0212 -#define IGU_ADDR_MSI_DATA 0x0213 - -#define IGU_INT_ENABLE 0 -#define IGU_INT_DISABLE 1 -#define IGU_INT_NOP 2 -#define IGU_INT_NOP2 3 +/* Host coalescing constants */ /* index numbers */ -#define HC_USTORM_DEF_SB_NUM_INDICES 4 -#define HC_CSTORM_DEF_SB_NUM_INDICES 8 -#define HC_XSTORM_DEF_SB_NUM_INDICES 4 -#define HC_TSTORM_DEF_SB_NUM_INDICES 4 -#define HC_USTORM_SB_NUM_INDICES 4 -#define HC_CSTORM_SB_NUM_INDICES 4 +#define HC_USTORM_DEF_SB_NUM_INDICES 4 +#define HC_CSTORM_DEF_SB_NUM_INDICES 8 +#define HC_XSTORM_DEF_SB_NUM_INDICES 4 +#define HC_TSTORM_DEF_SB_NUM_INDICES 4 +#define HC_USTORM_SB_NUM_INDICES 4 +#define HC_CSTORM_SB_NUM_INDICES 4 /* index values - which counterto update */ -#define HC_INDEX_U_ETH_RX_CQ_CONS 1 +#define HC_INDEX_U_TOE_RX_CQ_CONS 0 +#define HC_INDEX_U_ETH_RX_CQ_CONS 1 +#define HC_INDEX_U_ETH_RX_BD_CONS 2 +#define HC_INDEX_U_FCOE_EQ_CONS 3 + +#define HC_INDEX_C_TOE_TX_CQ_CONS 0 +#define HC_INDEX_C_ETH_TX_CQ_CONS 1 +#define HC_INDEX_C_ISCSI_EQ_CONS 2 + +#define HC_INDEX_DEF_X_SPQ_CONS 0 -#define HC_INDEX_C_ETH_TX_CQ_CONS 1 +#define HC_INDEX_DEF_C_RDMA_EQ_CONS 0 +#define HC_INDEX_DEF_C_RDMA_NAL_PROD 1 +#define HC_INDEX_DEF_C_ETH_FW_TX_CQ_CONS 2 +#define HC_INDEX_DEF_C_ETH_SLOW_PATH 3 +#define HC_INDEX_DEF_C_ETH_RDMA_CQ_CONS 4 +#define HC_INDEX_DEF_C_ETH_ISCSI_CQ_CONS 5 -#define HC_INDEX_DEF_X_SPQ_CONS 0 +#define HC_INDEX_DEF_U_ETH_RDMA_RX_CQ_CONS 0 +#define HC_INDEX_DEF_U_ETH_ISCSI_RX_CQ_CONS 1 +#define HC_INDEX_DEF_U_ETH_RDMA_RX_BD_CONS 2 +#define HC_INDEX_DEF_U_ETH_ISCSI_RX_BD_CONS 3 -#define HC_INDEX_DEF_C_ETH_FW_TX_CQ_CONS 2 -#define HC_INDEX_DEF_C_ETH_SLOW_PATH 3 /* used by the driver to get the SB offset */ -#define USTORM_ID 0 -#define CSTORM_ID 1 -#define XSTORM_ID 2 -#define TSTORM_ID 3 -#define ATTENTION_ID 4 +#define USTORM_ID 0 +#define CSTORM_ID 1 +#define XSTORM_ID 2 +#define TSTORM_ID 3 +#define ATTENTION_ID 4 /* max number of slow path commands per port */ -#define MAX_RAMRODS_PER_PORT (8) +#define MAX_RAMRODS_PER_PORT (8) /* values for RX ETH CQE type field */ -#define RX_ETH_CQE_TYPE_ETH_FASTPATH (0) -#define RX_ETH_CQE_TYPE_ETH_RAMROD (1) - -/* MAC address list size */ -#define T_MAC_ADDRESS_LIST_SIZE (96) - +#define RX_ETH_CQE_TYPE_ETH_FASTPATH (0) +#define RX_ETH_CQE_TYPE_ETH_RAMROD (1) + + +/**** DEFINES FOR TIMERS/CLOCKS RESOLUTIONS ****/ +#define EMULATION_FREQUENCY_FACTOR (1600) +#define FPGA_FREQUENCY_FACTOR (100) + +#define TIMERS_TICK_SIZE_CHIP (1e-3) +#define TIMERS_TICK_SIZE_EMUL \ + ((TIMERS_TICK_SIZE_CHIP)/((EMULATION_FREQUENCY_FACTOR))) +#define TIMERS_TICK_SIZE_FPGA \ + ((TIMERS_TICK_SIZE_CHIP)/((FPGA_FREQUENCY_FACTOR))) + +#define TSEMI_CLK1_RESUL_CHIP (1e-3) +#define TSEMI_CLK1_RESUL_EMUL \ + ((TSEMI_CLK1_RESUL_CHIP)/(EMULATION_FREQUENCY_FACTOR)) +#define TSEMI_CLK1_RESUL_FPGA \ + ((TSEMI_CLK1_RESUL_CHIP)/(FPGA_FREQUENCY_FACTOR)) + +#define USEMI_CLK1_RESUL_CHIP \ + (TIMERS_TICK_SIZE_CHIP) +#define USEMI_CLK1_RESUL_EMUL \ + (TIMERS_TICK_SIZE_EMUL) +#define USEMI_CLK1_RESUL_FPGA \ + (TIMERS_TICK_SIZE_FPGA) + +#define XSEMI_CLK1_RESUL_CHIP (1e-3) +#define XSEMI_CLK1_RESUL_EMUL \ + ((XSEMI_CLK1_RESUL_CHIP)/(EMULATION_FREQUENCY_FACTOR)) +#define XSEMI_CLK1_RESUL_FPGA \ + ((XSEMI_CLK1_RESUL_CHIP)/(FPGA_FREQUENCY_FACTOR)) + +#define XSEMI_CLK2_RESUL_CHIP (1e-6) +#define XSEMI_CLK2_RESUL_EMUL \ + ((XSEMI_CLK2_RESUL_CHIP)/(EMULATION_FREQUENCY_FACTOR)) +#define XSEMI_CLK2_RESUL_FPGA \ + ((XSEMI_CLK2_RESUL_CHIP)/(FPGA_FREQUENCY_FACTOR)) + +#define SDM_TIMER_TICK_RESUL_CHIP (4*(1e-6)) +#define SDM_TIMER_TICK_RESUL_EMUL \ + ((SDM_TIMER_TICK_RESUL_CHIP)/(EMULATION_FREQUENCY_FACTOR)) +#define SDM_TIMER_TICK_RESUL_FPGA \ + ((SDM_TIMER_TICK_RESUL_CHIP)/(FPGA_FREQUENCY_FACTOR)) + + +/**** END DEFINES FOR TIMERS/CLOCKS RESOLUTIONS ****/ #define XSTORM_IP_ID_ROLL_HALF 0x8000 #define XSTORM_IP_ID_ROLL_ALL 0 -#define FW_LOG_LIST_SIZE (50) +#define FW_LOG_LIST_SIZE (50) + +#define NUM_OF_PROTOCOLS 4 +#define MAX_COS_NUMBER 16 +#define MAX_T_STAT_COUNTER_ID 18 +#define MAX_X_STAT_COUNTER_ID 18 -#define NUM_OF_PROTOCOLS 4 -#define MAX_COS_NUMBER 16 -#define MAX_T_STAT_COUNTER_ID 18 +#define UNKNOWN_ADDRESS 0 +#define UNICAST_ADDRESS 1 +#define MULTICAST_ADDRESS 2 +#define BROADCAST_ADDRESS 3 -#define T_FAIR 1 -#define FAIR_MEM 2 -#define RS_PERIODIC_TIMEOUT_IN_SDM_TICS 25 +#define SINGLE_FUNCTION 0 +#define MULTI_FUNCTION 1 -#define UNKNOWN_ADDRESS 0 -#define UNICAST_ADDRESS 1 -#define MULTICAST_ADDRESS 2 -#define BROADCAST_ADDRESS 3 +#define IP_V4 0 +#define IP_V6 1 diff --git a/drivers/net/bnx2x_hsi.h b/drivers/net/bnx2x_hsi.h index 96208ace1466..e515d68ea20f 100644 --- a/drivers/net/bnx2x_hsi.h +++ b/drivers/net/bnx2x_hsi.h @@ -132,6 +132,12 @@ struct shared_hw_cfg { /* NVRAM Offset */ #define SHARED_HW_CFG_BOARD_TYPE_BCM957710T1003G 0x00000008 #define SHARED_HW_CFG_BOARD_TYPE_BCM957710A1022G 0x00000009 #define SHARED_HW_CFG_BOARD_TYPE_BCM957710A1021G 0x0000000a +#define SHARED_HW_CFG_BOARD_TYPE_BCM957710A1023G 0x0000000b +#define SHARED_HW_CFG_BOARD_TYPE_BCM957710A1033G 0x0000000c +#define SHARED_HW_CFG_BOARD_TYPE_BCM957711T1101 0x0000000d +#define SHARED_HW_CFG_BOARD_TYPE_BCM957711ET1201 0x0000000e +#define SHARED_HW_CFG_BOARD_TYPE_BCM957711A1133G 0x0000000f +#define SHARED_HW_CFG_BOARD_TYPE_BCM957711EA1233G 0x00000010 #define SHARED_HW_CFG_BOARD_VER_MASK 0xffff0000 #define SHARED_HW_CFG_BOARD_VER_SHIFT 16 @@ -313,6 +319,7 @@ struct shared_feat_cfg { /* NVRAM Offset */ u32 config; /* 0x450 */ #define SHARED_FEATURE_BMC_ECHO_MODE_EN 0x00000001 +#define SHARED_FEATURE_MF_MODE_DISABLED 0x00000100 }; @@ -502,20 +509,20 @@ struct port_feat_cfg { /* port 0: 0x454 port 1: 0x4c8 */ }; -/***************************************************************************** - * Device Information * - *****************************************************************************/ -struct dev_info { /* size */ +/**************************************************************************** + * Device Information * + ****************************************************************************/ +struct dev_info { /* size */ - u32 bc_rev; /* 8 bits each: major, minor, build */ /* 4 */ + u32 bc_rev; /* 8 bits each: major, minor, build */ /* 4 */ - struct shared_hw_cfg shared_hw_config; /* 40 */ + struct shared_hw_cfg shared_hw_config; /* 40 */ - struct port_hw_cfg port_hw_config[PORT_MAX]; /* 400*2=800 */ + struct port_hw_cfg port_hw_config[PORT_MAX]; /* 400*2=800 */ - struct shared_feat_cfg shared_feature_config; /* 4 */ + struct shared_feat_cfg shared_feature_config; /* 4 */ - struct port_feat_cfg port_feature_config[PORT_MAX]; /* 116*2=232 */ + struct port_feat_cfg port_feature_config[PORT_MAX];/* 116*2=232 */ }; @@ -632,7 +639,9 @@ struct drv_port_mb { #define LINK_STATUS_LINK_PARTNER_15GXFD_CAPABLE 0x08000000 #define LINK_STATUS_LINK_PARTNER_16GXFD_CAPABLE 0x10000000 - u32 reserved[3]; + u32 port_stx; + + u32 reserved[2]; }; @@ -655,6 +664,11 @@ struct drv_func_mb { #define DRV_MSG_CODE_GET_MANUF_KEY 0x82000000 #define DRV_MSG_CODE_LOAD_L2B_PRAM 0x90000000 +#define BIOS_MSG_CODE_LIC_CHALLENGE 0xff010000 +#define BIOS_MSG_CODE_LIC_RESPONSE 0xff020000 +#define BIOS_MSG_CODE_VIRT_MAC_PRIM 0xff030000 +#define BIOS_MSG_CODE_VIRT_MAC_ISCSI 0xff040000 + #define DRV_MSG_SEQ_NUMBER_MASK 0x0000ffff u32 drv_mb_param; @@ -684,6 +698,11 @@ struct drv_func_mb { #define FW_MSG_CODE_L2B_PRAM_X_LOAD_FAILURE 0x90230000 #define FW_MSG_CODE_L2B_PRAM_U_LOAD_FAILURE 0x90240000 +#define FW_MSG_CODE_LIC_CHALLENGE 0xff010000 +#define FW_MSG_CODE_LIC_RESPONSE 0xff020000 +#define FW_MSG_CODE_VIRT_MAC_PRIM 0xff030000 +#define FW_MSG_CODE_VIRT_MAC_ISCSI 0xff040000 + #define FW_MSG_SEQ_NUMBER_MASK 0x0000ffff u32 fw_mb_param; @@ -709,7 +728,13 @@ struct drv_func_mb { u32 iscsi_boot_signature; u32 iscsi_boot_block_offset; - u32 reserved[3]; + u32 drv_status; +#define DRV_STATUS_PMF 0x00000001 + + u32 virt_mac_upper; +#define VIRT_MAC_SIGN_MASK 0xffff0000 +#define VIRT_MAC_SIGNATURE 0x564d0000 + u32 virt_mac_lower; }; @@ -725,6 +750,92 @@ struct mgmtfw_state { }; +/**************************************************************************** + * Multi-Function configuration * + ****************************************************************************/ +struct shared_mf_cfg { + + u32 clp_mb; +#define SHARED_MF_CLP_SET_DEFAULT 0x00000000 + /* set by CLP */ +#define SHARED_MF_CLP_EXIT 0x00000001 + /* set by MCP */ +#define SHARED_MF_CLP_EXIT_DONE 0x00010000 + +}; + +struct port_mf_cfg { + + u32 dynamic_cfg; /* device control channel */ +#define PORT_MF_CFG_OUTER_VLAN_TAG_MASK 0x0000ffff +#define PORT_MF_CFG_OUTER_VLAN_TAG_SHIFT 0 +#define PORT_MF_CFG_DYNAMIC_CFG_ENABLED 0x00010000 +#define PORT_MF_CFG_DYNAMIC_CFG_DEFAULT 0x00000000 + + u32 reserved[3]; + +}; + +struct func_mf_cfg { + + u32 config; + /* E/R/I/D */ + /* function 0 of each port cannot be hidden */ +#define FUNC_MF_CFG_FUNC_HIDE 0x00000001 + +#define FUNC_MF_CFG_PROTOCOL_MASK 0x00000007 +#define FUNC_MF_CFG_PROTOCOL_ETHERNET 0x00000002 +#define FUNC_MF_CFG_PROTOCOL_ETHERNET_WITH_RDMA 0x00000004 +#define FUNC_MF_CFG_PROTOCOL_ISCSI 0x00000006 +#define FUNC_MF_CFG_PROTOCOL_DEFAULT\ + FUNC_MF_CFG_PROTOCOL_ETHERNET_WITH_RDMA + +#define FUNC_MF_CFG_FUNC_DISABLED 0x00000008 + + /* PRI */ + /* 0 - low priority, 3 - high priority */ +#define FUNC_MF_CFG_TRANSMIT_PRIORITY_MASK 0x00000300 +#define FUNC_MF_CFG_TRANSMIT_PRIORITY_SHIFT 8 +#define FUNC_MF_CFG_TRANSMIT_PRIORITY_DEFAULT 0x00000000 + + /* MINBW, MAXBW */ + /* value range - 0..100, increments in 100Mbps */ +#define FUNC_MF_CFG_MIN_BW_MASK 0x00ff0000 +#define FUNC_MF_CFG_MIN_BW_SHIFT 16 +#define FUNC_MF_CFG_MIN_BW_DEFAULT 0x00000000 +#define FUNC_MF_CFG_MAX_BW_MASK 0xff000000 +#define FUNC_MF_CFG_MAX_BW_SHIFT 24 +#define FUNC_MF_CFG_MAX_BW_DEFAULT 0x64000000 + + u32 mac_upper; /* MAC */ +#define FUNC_MF_CFG_UPPERMAC_MASK 0x0000ffff +#define FUNC_MF_CFG_UPPERMAC_SHIFT 0 +#define FUNC_MF_CFG_UPPERMAC_DEFAULT FUNC_MF_CFG_UPPERMAC_MASK + u32 mac_lower; +#define FUNC_MF_CFG_LOWERMAC_DEFAULT 0xffffffff + + u32 e1hov_tag; /* VNI */ +#define FUNC_MF_CFG_E1HOV_TAG_MASK 0x0000ffff +#define FUNC_MF_CFG_E1HOV_TAG_SHIFT 0 +#define FUNC_MF_CFG_E1HOV_TAG_DEFAULT FUNC_MF_CFG_E1HOV_TAG_MASK + + u32 reserved[2]; + +}; + +struct mf_cfg { + + struct shared_mf_cfg shared_mf_config; + struct port_mf_cfg port_mf_config[PORT_MAX]; +#if defined(b710) + struct func_mf_cfg func_mf_config[E1_FUNC_MAX]; +#else + struct func_mf_cfg func_mf_config[E1H_FUNC_MAX]; +#endif + +}; + + /**************************************************************************** * Shared Memory Region * ****************************************************************************/ @@ -760,18 +871,18 @@ struct shmem_region { /* SharedMem Offset (size) */ struct mgmtfw_state mgmtfw_state; /* 0x4ac (0x1b8) */ struct drv_port_mb port_mb[PORT_MAX]; /* 0x664 (16*2=0x20) */ -#if defined(b710) - struct drv_func_mb func_mb[E1_FUNC_MAX]; /* 0x684 (44*2=0x58) */ -#else struct drv_func_mb func_mb[E1H_FUNC_MAX]; -#endif + + struct mf_cfg mf_cfg; }; /* 0x6dc */ + + #define BCM_5710_FW_MAJOR_VERSION 4 -#define BCM_5710_FW_MINOR_VERSION 0 -#define BCM_5710_FW_REVISION_VERSION 14 +#define BCM_5710_FW_MINOR_VERSION 5 +#define BCM_5710_FW_REVISION_VERSION 1 #define BCM_5710_FW_COMPILE_FLAGS 1 @@ -810,7 +921,7 @@ struct doorbell_hdr { }; /* - * doorbell message send to the chip + * doorbell message sent to the chip */ struct doorbell { #if defined(__BIG_ENDIAN) @@ -866,8 +977,10 @@ struct parsing_flags { u16 flags; #define PARSING_FLAGS_ETHERNET_ADDRESS_TYPE (0x1<<0) #define PARSING_FLAGS_ETHERNET_ADDRESS_TYPE_SHIFT 0 -#define PARSING_FLAGS_NUMBER_OF_NESTED_VLANS (0x3<<1) -#define PARSING_FLAGS_NUMBER_OF_NESTED_VLANS_SHIFT 1 +#define PARSING_FLAGS_VLAN (0x1<<1) +#define PARSING_FLAGS_VLAN_SHIFT 1 +#define PARSING_FLAGS_EXTRA_VLAN (0x1<<2) +#define PARSING_FLAGS_EXTRA_VLAN_SHIFT 2 #define PARSING_FLAGS_OVER_ETHERNET_PROTOCOL (0x3<<3) #define PARSING_FLAGS_OVER_ETHERNET_PROTOCOL_SHIFT 3 #define PARSING_FLAGS_IP_OPTIONS (0x1<<5) @@ -891,6 +1004,12 @@ struct parsing_flags { }; +struct regpair { + u32 lo; + u32 hi; +}; + + /* * dmae command structure */ @@ -971,72 +1090,107 @@ struct double_regpair { /* - * The eth Rx Buffer Descriptor + * The eth storm context of Ustorm (configuration part) */ -struct eth_rx_bd { - u32 addr_lo; - u32 addr_hi; -}; - -/* - * The eth storm context of Ustorm - */ -struct ustorm_eth_st_context { +struct ustorm_eth_st_context_config { #if defined(__BIG_ENDIAN) - u8 sb_index_number; + u8 flags; +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_MC_ALIGNMENT (0x1<<0) +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_MC_ALIGNMENT_SHIFT 0 +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_DYNAMIC_HC (0x1<<1) +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_DYNAMIC_HC_SHIFT 1 +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_TPA (0x1<<2) +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_TPA_SHIFT 2 +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_SGE_RING (0x1<<3) +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_SGE_RING_SHIFT 3 +#define __USTORM_ETH_ST_CONTEXT_CONFIG_RESERVED0 (0xF<<4) +#define __USTORM_ETH_ST_CONTEXT_CONFIG_RESERVED0_SHIFT 4 u8 status_block_id; - u8 __local_rx_bd_cons; - u8 __local_rx_bd_prod; + u8 clientId; + u8 sb_index_numbers; +#define USTORM_ETH_ST_CONTEXT_CONFIG_CQE_SB_INDEX_NUMBER (0xF<<0) +#define USTORM_ETH_ST_CONTEXT_CONFIG_CQE_SB_INDEX_NUMBER_SHIFT 0 +#define USTORM_ETH_ST_CONTEXT_CONFIG_BD_SB_INDEX_NUMBER (0xF<<4) +#define USTORM_ETH_ST_CONTEXT_CONFIG_BD_SB_INDEX_NUMBER_SHIFT 4 #elif defined(__LITTLE_ENDIAN) - u8 __local_rx_bd_prod; - u8 __local_rx_bd_cons; + u8 sb_index_numbers; +#define USTORM_ETH_ST_CONTEXT_CONFIG_CQE_SB_INDEX_NUMBER (0xF<<0) +#define USTORM_ETH_ST_CONTEXT_CONFIG_CQE_SB_INDEX_NUMBER_SHIFT 0 +#define USTORM_ETH_ST_CONTEXT_CONFIG_BD_SB_INDEX_NUMBER (0xF<<4) +#define USTORM_ETH_ST_CONTEXT_CONFIG_BD_SB_INDEX_NUMBER_SHIFT 4 + u8 clientId; u8 status_block_id; - u8 sb_index_number; + u8 flags; +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_MC_ALIGNMENT (0x1<<0) +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_MC_ALIGNMENT_SHIFT 0 +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_DYNAMIC_HC (0x1<<1) +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_DYNAMIC_HC_SHIFT 1 +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_TPA (0x1<<2) +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_TPA_SHIFT 2 +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_SGE_RING (0x1<<3) +#define USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_SGE_RING_SHIFT 3 +#define __USTORM_ETH_ST_CONTEXT_CONFIG_RESERVED0 (0xF<<4) +#define __USTORM_ETH_ST_CONTEXT_CONFIG_RESERVED0_SHIFT 4 #endif #if defined(__BIG_ENDIAN) - u16 rcq_cons; - u16 rx_bd_cons; + u16 bd_buff_size; + u16 mc_alignment_size; #elif defined(__LITTLE_ENDIAN) - u16 rx_bd_cons; - u16 rcq_cons; + u16 mc_alignment_size; + u16 bd_buff_size; #endif - u32 rx_bd_page_base_lo; - u32 rx_bd_page_base_hi; - u32 rcq_base_address_lo; - u32 rcq_base_address_hi; #if defined(__BIG_ENDIAN) - u16 __num_of_returned_cqes; - u8 num_rss; - u8 flags; -#define USTORM_ETH_ST_CONTEXT_ENABLE_MC_ALIGNMENT (0x1<<0) -#define USTORM_ETH_ST_CONTEXT_ENABLE_MC_ALIGNMENT_SHIFT 0 -#define USTORM_ETH_ST_CONTEXT_ENABLE_DYNAMIC_HC (0x1<<1) -#define USTORM_ETH_ST_CONTEXT_ENABLE_DYNAMIC_HC_SHIFT 1 -#define USTORM_ETH_ST_CONTEXT_ENABLE_TPA (0x1<<2) -#define USTORM_ETH_ST_CONTEXT_ENABLE_TPA_SHIFT 2 -#define __USTORM_ETH_ST_CONTEXT_RESERVED0 (0x1F<<3) -#define __USTORM_ETH_ST_CONTEXT_RESERVED0_SHIFT 3 + u8 __local_sge_prod; + u8 __local_bd_prod; + u16 sge_buff_size; #elif defined(__LITTLE_ENDIAN) - u8 flags; -#define USTORM_ETH_ST_CONTEXT_ENABLE_MC_ALIGNMENT (0x1<<0) -#define USTORM_ETH_ST_CONTEXT_ENABLE_MC_ALIGNMENT_SHIFT 0 -#define USTORM_ETH_ST_CONTEXT_ENABLE_DYNAMIC_HC (0x1<<1) -#define USTORM_ETH_ST_CONTEXT_ENABLE_DYNAMIC_HC_SHIFT 1 -#define USTORM_ETH_ST_CONTEXT_ENABLE_TPA (0x1<<2) -#define USTORM_ETH_ST_CONTEXT_ENABLE_TPA_SHIFT 2 -#define __USTORM_ETH_ST_CONTEXT_RESERVED0 (0x1F<<3) -#define __USTORM_ETH_ST_CONTEXT_RESERVED0_SHIFT 3 - u8 num_rss; - u16 __num_of_returned_cqes; + u16 sge_buff_size; + u8 __local_bd_prod; + u8 __local_sge_prod; #endif #if defined(__BIG_ENDIAN) - u16 mc_alignment_size; - u16 agg_threshold; + u16 __bd_cons; + u16 __sge_cons; #elif defined(__LITTLE_ENDIAN) - u16 agg_threshold; - u16 mc_alignment_size; + u16 __sge_cons; + u16 __bd_cons; #endif + u32 bd_page_base_lo; + u32 bd_page_base_hi; + u32 sge_page_base_lo; + u32 sge_page_base_hi; +}; + +/* + * The eth Rx Buffer Descriptor + */ +struct eth_rx_bd { + u32 addr_lo; + u32 addr_hi; +}; + +/* + * The eth Rx SGE Descriptor + */ +struct eth_rx_sge { + u32 addr_lo; + u32 addr_hi; +}; + +/* + * Local BDs and SGEs rings (in ETH) + */ +struct eth_local_rx_rings { struct eth_rx_bd __local_bd_ring[16]; + struct eth_rx_sge __local_sge_ring[12]; +}; + +/* + * The eth storm context of Ustorm + */ +struct ustorm_eth_st_context { + struct ustorm_eth_st_context_config common; + struct eth_local_rx_rings __rings; }; /* @@ -1107,9 +1261,9 @@ struct xstorm_eth_extra_ag_context_section { #if defined(__BIG_ENDIAN) u16 __reserved3; u8 __reserved2; - u8 __agg_misc7; + u8 __da_only_cnt; #elif defined(__LITTLE_ENDIAN) - u8 __agg_misc7; + u8 __da_only_cnt; u8 __reserved2; u16 __reserved3; #endif @@ -1387,7 +1541,13 @@ struct timers_block_context { u32 __reserved_0; u32 __reserved_1; u32 __reserved_2; - u32 __reserved_flags; + u32 flags; +#define __TIMERS_BLOCK_CONTEXT_NUM_OF_ACTIVE_TIMERS (0x3<<0) +#define __TIMERS_BLOCK_CONTEXT_NUM_OF_ACTIVE_TIMERS_SHIFT 0 +#define TIMERS_BLOCK_CONTEXT_CONN_VALID_FLG (0x1<<2) +#define TIMERS_BLOCK_CONTEXT_CONN_VALID_FLG_SHIFT 2 +#define __TIMERS_BLOCK_CONTEXT_RESERVED0 (0x1FFFFFFF<<3) +#define __TIMERS_BLOCK_CONTEXT_RESERVED0_SHIFT 3 }; /* @@ -1497,11 +1657,19 @@ struct xstorm_eth_st_context { u32 tx_bd_page_base_hi; #if defined(__BIG_ENDIAN) u16 tx_bd_cons; - u8 __reserved0; + u8 statistics_data; +#define XSTORM_ETH_ST_CONTEXT_STATISTICS_COUNTER_ID (0x7F<<0) +#define XSTORM_ETH_ST_CONTEXT_STATISTICS_COUNTER_ID_SHIFT 0 +#define XSTORM_ETH_ST_CONTEXT_STATISTICS_ENABLE (0x1<<7) +#define XSTORM_ETH_ST_CONTEXT_STATISTICS_ENABLE_SHIFT 7 u8 __local_tx_bd_prod; #elif defined(__LITTLE_ENDIAN) u8 __local_tx_bd_prod; - u8 __reserved0; + u8 statistics_data; +#define XSTORM_ETH_ST_CONTEXT_STATISTICS_COUNTER_ID (0x7F<<0) +#define XSTORM_ETH_ST_CONTEXT_STATISTICS_COUNTER_ID_SHIFT 0 +#define XSTORM_ETH_ST_CONTEXT_STATISTICS_ENABLE (0x1<<7) +#define XSTORM_ETH_ST_CONTEXT_STATISTICS_ENABLE_SHIFT 7 u16 tx_bd_cons; #endif u32 db_data_addr_lo; @@ -1578,7 +1746,7 @@ struct eth_tx_doorbell { struct ustorm_def_status_block { u16 index_values[HC_USTORM_DEF_SB_NUM_INDICES]; u16 status_block_index; - u8 reserved0; + u8 func; u8 status_block_id; u32 __flags; }; @@ -1589,7 +1757,7 @@ struct ustorm_def_status_block { struct cstorm_def_status_block { u16 index_values[HC_CSTORM_DEF_SB_NUM_INDICES]; u16 status_block_index; - u8 reserved0; + u8 func; u8 status_block_id; u32 __flags; }; @@ -1600,7 +1768,7 @@ struct cstorm_def_status_block { struct xstorm_def_status_block { u16 index_values[HC_XSTORM_DEF_SB_NUM_INDICES]; u16 status_block_index; - u8 reserved0; + u8 func; u8 status_block_id; u32 __flags; }; @@ -1611,7 +1779,7 @@ struct xstorm_def_status_block { struct tstorm_def_status_block { u16 index_values[HC_TSTORM_DEF_SB_NUM_INDICES]; u16 status_block_index; - u8 reserved0; + u8 func; u8 status_block_id; u32 __flags; }; @@ -1634,7 +1802,7 @@ struct host_def_status_block { struct ustorm_status_block { u16 index_values[HC_USTORM_SB_NUM_INDICES]; u16 status_block_index; - u8 reserved0; + u8 func; u8 status_block_id; u32 __flags; }; @@ -1645,7 +1813,7 @@ struct ustorm_status_block { struct cstorm_status_block { u16 index_values[HC_CSTORM_SB_NUM_INDICES]; u16 status_block_index; - u8 reserved0; + u8 func; u8 status_block_id; u32 __flags; }; @@ -1683,20 +1851,21 @@ struct eth_dynamic_hc_config { * regular eth FP CQE parameters struct */ struct eth_fast_path_rx_cqe { - u8 type; - u8 error_type_flags; -#define ETH_FAST_PATH_RX_CQE_PHY_DECODE_ERR_FLG (0x1<<0) -#define ETH_FAST_PATH_RX_CQE_PHY_DECODE_ERR_FLG_SHIFT 0 -#define ETH_FAST_PATH_RX_CQE_IP_BAD_XSUM_FLG (0x1<<1) -#define ETH_FAST_PATH_RX_CQE_IP_BAD_XSUM_FLG_SHIFT 1 -#define ETH_FAST_PATH_RX_CQE_L4_BAD_XSUM_FLG (0x1<<2) -#define ETH_FAST_PATH_RX_CQE_L4_BAD_XSUM_FLG_SHIFT 2 -#define ETH_FAST_PATH_RX_CQE_START_FLG (0x1<<3) -#define ETH_FAST_PATH_RX_CQE_START_FLG_SHIFT 3 -#define ETH_FAST_PATH_RX_CQE_END_FLG (0x1<<4) -#define ETH_FAST_PATH_RX_CQE_END_FLG_SHIFT 4 -#define ETH_FAST_PATH_RX_CQE_RESERVED0 (0x7<<5) -#define ETH_FAST_PATH_RX_CQE_RESERVED0_SHIFT 5 + u8 type_error_flags; +#define ETH_FAST_PATH_RX_CQE_TYPE (0x1<<0) +#define ETH_FAST_PATH_RX_CQE_TYPE_SHIFT 0 +#define ETH_FAST_PATH_RX_CQE_PHY_DECODE_ERR_FLG (0x1<<1) +#define ETH_FAST_PATH_RX_CQE_PHY_DECODE_ERR_FLG_SHIFT 1 +#define ETH_FAST_PATH_RX_CQE_IP_BAD_XSUM_FLG (0x1<<2) +#define ETH_FAST_PATH_RX_CQE_IP_BAD_XSUM_FLG_SHIFT 2 +#define ETH_FAST_PATH_RX_CQE_L4_BAD_XSUM_FLG (0x1<<3) +#define ETH_FAST_PATH_RX_CQE_L4_BAD_XSUM_FLG_SHIFT 3 +#define ETH_FAST_PATH_RX_CQE_START_FLG (0x1<<4) +#define ETH_FAST_PATH_RX_CQE_START_FLG_SHIFT 4 +#define ETH_FAST_PATH_RX_CQE_END_FLG (0x1<<5) +#define ETH_FAST_PATH_RX_CQE_END_FLG_SHIFT 5 +#define ETH_FAST_PATH_RX_CQE_RESERVED0 (0x3<<6) +#define ETH_FAST_PATH_RX_CQE_RESERVED0_SHIFT 6 u8 status_flags; #define ETH_FAST_PATH_RX_CQE_RSS_HASH_TYPE (0x7<<0) #define ETH_FAST_PATH_RX_CQE_RSS_HASH_TYPE_SHIFT 0 @@ -1711,11 +1880,13 @@ struct eth_fast_path_rx_cqe { #define ETH_FAST_PATH_RX_CQE_L4_XSUM_NO_VALIDATION_FLG (0x1<<7) #define ETH_FAST_PATH_RX_CQE_L4_XSUM_NO_VALIDATION_FLG_SHIFT 7 u8 placement_offset; + u8 queue_index; u32 rss_hash_result; u16 vlan_tag; u16 pkt_len; - u16 queue_index; + u16 len_on_bd; struct parsing_flags pars_flags; + u16 sgl[8]; }; @@ -1728,6 +1899,23 @@ struct eth_halt_ramrod_data { }; +/* + * The data for statistics query ramrod + */ +struct eth_query_ramrod_data { +#if defined(__BIG_ENDIAN) + u8 reserved0; + u8 collect_port_1b; + u16 drv_counter; +#elif defined(__LITTLE_ENDIAN) + u16 drv_counter; + u8 collect_port_1b; + u8 reserved0; +#endif + u32 ctr_id_vector; +}; + + /* * Place holder for ramrods protocol specific data */ @@ -1758,15 +1946,20 @@ struct eth_rx_bd_next_page { * Eth Rx Cqe structure- general structure for ramrods */ struct common_ramrod_eth_rx_cqe { - u8 type; + u8 ramrod_type; +#define COMMON_RAMROD_ETH_RX_CQE_TYPE (0x1<<0) +#define COMMON_RAMROD_ETH_RX_CQE_TYPE_SHIFT 0 +#define COMMON_RAMROD_ETH_RX_CQE_RESERVED0 (0x7F<<1) +#define COMMON_RAMROD_ETH_RX_CQE_RESERVED0_SHIFT 1 u8 conn_type_3b; - u16 reserved; + u16 reserved1; u32 conn_and_cmd_data; #define COMMON_RAMROD_ETH_RX_CQE_CID (0xFFFFFF<<0) #define COMMON_RAMROD_ETH_RX_CQE_CID_SHIFT 0 #define COMMON_RAMROD_ETH_RX_CQE_CMD_ID (0xFF<<24) #define COMMON_RAMROD_ETH_RX_CQE_CMD_ID_SHIFT 24 struct ramrod_data protocol_data; + u32 reserved2[4]; }; /* @@ -1775,8 +1968,7 @@ struct common_ramrod_eth_rx_cqe { struct eth_rx_cqe_next_page { u32 addr_lo; u32 addr_hi; - u32 reserved0; - u32 reserved1; + u32 reserved[6]; }; /* @@ -1806,11 +1998,6 @@ struct spe_hdr { u16 reserved; }; -struct regpair { - u32 lo; - u32 hi; -}; - /* * ethernet slow path element */ @@ -1821,6 +2008,7 @@ union eth_specific_data { struct eth_halt_ramrod_data halt_ramrod_data; struct regpair leading_cqe_addr; struct regpair update_data_addr; + struct eth_query_ramrod_data query_ramrod_data; }; /* @@ -1843,10 +2031,13 @@ struct eth_tx_db_data { /* - * Common configuration parameters per port in Tstorm + * Common configuration parameters per function in Tstorm */ struct tstorm_eth_function_common_config { - u32 config_flags; +#if defined(__BIG_ENDIAN) + u8 leading_client_id; + u8 rss_result_mask; + u16 config_flags; #define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_CAPABILITY (0x1<<0) #define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_CAPABILITY_SHIFT 0 #define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_TCP_CAPABILITY (0x1<<1) @@ -1859,17 +2050,32 @@ struct tstorm_eth_function_common_config { #define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_ENABLE_SHIFT 4 #define TSTORM_ETH_FUNCTION_COMMON_CONFIG_DEFAULT_ENABLE (0x1<<5) #define TSTORM_ETH_FUNCTION_COMMON_CONFIG_DEFAULT_ENABLE_SHIFT 5 -#define __TSTORM_ETH_FUNCTION_COMMON_CONFIG_RESERVED0 (0x3FFFFFF<<6) -#define __TSTORM_ETH_FUNCTION_COMMON_CONFIG_RESERVED0_SHIFT 6 -#if defined(__BIG_ENDIAN) - u16 __secondary_vlan_id; - u8 leading_client_id; - u8 rss_result_mask; +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_VLAN_IN_CAM (0x1<<6) +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_VLAN_IN_CAM_SHIFT 6 +#define __TSTORM_ETH_FUNCTION_COMMON_CONFIG_RESERVED0 (0x1FF<<7) +#define __TSTORM_ETH_FUNCTION_COMMON_CONFIG_RESERVED0_SHIFT 7 #elif defined(__LITTLE_ENDIAN) + u16 config_flags; +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_CAPABILITY (0x1<<0) +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_CAPABILITY_SHIFT 0 +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_TCP_CAPABILITY (0x1<<1) +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV4_TCP_CAPABILITY_SHIFT 1 +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV6_CAPABILITY (0x1<<2) +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV6_CAPABILITY_SHIFT 2 +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV6_TCP_CAPABILITY (0x1<<3) +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_IPV6_TCP_CAPABILITY_SHIFT 3 +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_ENABLE (0x1<<4) +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_RSS_ENABLE_SHIFT 4 +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_DEFAULT_ENABLE (0x1<<5) +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_DEFAULT_ENABLE_SHIFT 5 +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_VLAN_IN_CAM (0x1<<6) +#define TSTORM_ETH_FUNCTION_COMMON_CONFIG_VLAN_IN_CAM_SHIFT 6 +#define __TSTORM_ETH_FUNCTION_COMMON_CONFIG_RESERVED0 (0x1FF<<7) +#define __TSTORM_ETH_FUNCTION_COMMON_CONFIG_RESERVED0_SHIFT 7 u8 rss_result_mask; u8 leading_client_id; - u16 __secondary_vlan_id; #endif + u16 vlan_id[2]; }; /* @@ -1887,7 +2093,7 @@ struct eth_update_ramrod_data { struct mac_configuration_hdr { u8 length_6b; u8 offset; - u16 reserved0; + u16 client_id; u32 reserved1; }; @@ -1943,16 +2149,56 @@ struct mac_configuration_cmd { }; +/* + * MAC address in list for ramrod + */ +struct mac_configuration_entry_e1h { + u16 lsb_mac_addr; + u16 middle_mac_addr; + u16 msb_mac_addr; + u16 vlan_id; + u16 e1hov_id; + u8 client_id; + u8 flags; +#define MAC_CONFIGURATION_ENTRY_E1H_PORT (0x1<<0) +#define MAC_CONFIGURATION_ENTRY_E1H_PORT_SHIFT 0 +#define MAC_CONFIGURATION_ENTRY_E1H_ACTION_TYPE (0x1<<1) +#define MAC_CONFIGURATION_ENTRY_E1H_ACTION_TYPE_SHIFT 1 +#define MAC_CONFIGURATION_ENTRY_E1H_RDMA_MAC (0x1<<2) +#define MAC_CONFIGURATION_ENTRY_E1H_RDMA_MAC_SHIFT 2 +#define MAC_CONFIGURATION_ENTRY_E1H_RESERVED0 (0x1F<<3) +#define MAC_CONFIGURATION_ENTRY_E1H_RESERVED0_SHIFT 3 +}; + +/* + * MAC filtering configuration command + */ +struct mac_configuration_cmd_e1h { + struct mac_configuration_hdr hdr; + struct mac_configuration_entry_e1h config_table[32]; +}; + + +/* + * approximate-match multicast filtering for E1H per function in Tstorm + */ +struct tstorm_eth_approximate_match_multicast_filtering { + u32 mcast_add_hash_bit_array[8]; +}; + + /* * Configuration parameters per client in Tstorm */ struct tstorm_eth_client_config { #if defined(__BIG_ENDIAN) - u16 statistics_counter_id; + u8 max_sges_for_packet; + u8 statistics_counter_id; u16 mtu; #elif defined(__LITTLE_ENDIAN) u16 mtu; - u16 statistics_counter_id; + u8 statistics_counter_id; + u8 max_sges_for_packet; #endif #if defined(__BIG_ENDIAN) u16 drop_flags; @@ -1960,42 +2206,42 @@ struct tstorm_eth_client_config { #define TSTORM_ETH_CLIENT_CONFIG_DROP_IP_CS_ERR_SHIFT 0 #define TSTORM_ETH_CLIENT_CONFIG_DROP_TCP_CS_ERR (0x1<<1) #define TSTORM_ETH_CLIENT_CONFIG_DROP_TCP_CS_ERR_SHIFT 1 -#define TSTORM_ETH_CLIENT_CONFIG_DROP_MAC_ERR (0x1<<2) -#define TSTORM_ETH_CLIENT_CONFIG_DROP_MAC_ERR_SHIFT 2 -#define TSTORM_ETH_CLIENT_CONFIG_DROP_TTL0 (0x1<<3) -#define TSTORM_ETH_CLIENT_CONFIG_DROP_TTL0_SHIFT 3 -#define TSTORM_ETH_CLIENT_CONFIG_DROP_UDP_CS_ERR (0x1<<4) -#define TSTORM_ETH_CLIENT_CONFIG_DROP_UDP_CS_ERR_SHIFT 4 -#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED1 (0x7FF<<5) -#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED1_SHIFT 5 +#define TSTORM_ETH_CLIENT_CONFIG_DROP_TTL0 (0x1<<2) +#define TSTORM_ETH_CLIENT_CONFIG_DROP_TTL0_SHIFT 2 +#define TSTORM_ETH_CLIENT_CONFIG_DROP_UDP_CS_ERR (0x1<<3) +#define TSTORM_ETH_CLIENT_CONFIG_DROP_UDP_CS_ERR_SHIFT 3 +#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED1 (0xFFF<<4) +#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED1_SHIFT 4 u16 config_flags; #define TSTORM_ETH_CLIENT_CONFIG_VLAN_REMOVAL_ENABLE (0x1<<0) #define TSTORM_ETH_CLIENT_CONFIG_VLAN_REMOVAL_ENABLE_SHIFT 0 #define TSTORM_ETH_CLIENT_CONFIG_STATSITICS_ENABLE (0x1<<1) #define TSTORM_ETH_CLIENT_CONFIG_STATSITICS_ENABLE_SHIFT 1 -#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED0 (0x3FFF<<2) -#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED0_SHIFT 2 +#define TSTORM_ETH_CLIENT_CONFIG_ENABLE_SGE_RING (0x1<<2) +#define TSTORM_ETH_CLIENT_CONFIG_ENABLE_SGE_RING_SHIFT 2 +#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED0 (0x1FFF<<3) +#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED0_SHIFT 3 #elif defined(__LITTLE_ENDIAN) u16 config_flags; #define TSTORM_ETH_CLIENT_CONFIG_VLAN_REMOVAL_ENABLE (0x1<<0) #define TSTORM_ETH_CLIENT_CONFIG_VLAN_REMOVAL_ENABLE_SHIFT 0 #define TSTORM_ETH_CLIENT_CONFIG_STATSITICS_ENABLE (0x1<<1) #define TSTORM_ETH_CLIENT_CONFIG_STATSITICS_ENABLE_SHIFT 1 -#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED0 (0x3FFF<<2) -#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED0_SHIFT 2 +#define TSTORM_ETH_CLIENT_CONFIG_ENABLE_SGE_RING (0x1<<2) +#define TSTORM_ETH_CLIENT_CONFIG_ENABLE_SGE_RING_SHIFT 2 +#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED0 (0x1FFF<<3) +#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED0_SHIFT 3 u16 drop_flags; #define TSTORM_ETH_CLIENT_CONFIG_DROP_IP_CS_ERR (0x1<<0) #define TSTORM_ETH_CLIENT_CONFIG_DROP_IP_CS_ERR_SHIFT 0 #define TSTORM_ETH_CLIENT_CONFIG_DROP_TCP_CS_ERR (0x1<<1) #define TSTORM_ETH_CLIENT_CONFIG_DROP_TCP_CS_ERR_SHIFT 1 -#define TSTORM_ETH_CLIENT_CONFIG_DROP_MAC_ERR (0x1<<2) -#define TSTORM_ETH_CLIENT_CONFIG_DROP_MAC_ERR_SHIFT 2 -#define TSTORM_ETH_CLIENT_CONFIG_DROP_TTL0 (0x1<<3) -#define TSTORM_ETH_CLIENT_CONFIG_DROP_TTL0_SHIFT 3 -#define TSTORM_ETH_CLIENT_CONFIG_DROP_UDP_CS_ERR (0x1<<4) -#define TSTORM_ETH_CLIENT_CONFIG_DROP_UDP_CS_ERR_SHIFT 4 -#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED1 (0x7FF<<5) -#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED1_SHIFT 5 +#define TSTORM_ETH_CLIENT_CONFIG_DROP_TTL0 (0x1<<2) +#define TSTORM_ETH_CLIENT_CONFIG_DROP_TTL0_SHIFT 2 +#define TSTORM_ETH_CLIENT_CONFIG_DROP_UDP_CS_ERR (0x1<<3) +#define TSTORM_ETH_CLIENT_CONFIG_DROP_UDP_CS_ERR_SHIFT 3 +#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED1 (0xFFF<<4) +#define __TSTORM_ETH_CLIENT_CONFIG_RESERVED1_SHIFT 4 #endif }; @@ -2011,96 +2257,112 @@ struct tstorm_eth_mac_filter_config { u32 bcast_drop_all; u32 bcast_accept_all; u32 strict_vlan; - u32 __secondary_vlan_clients; + u32 vlan_filter[2]; + u32 reserved; }; -struct rate_shaping_per_protocol { +/* + * Three RX producers for ETH + */ +struct tstorm_eth_rx_producers { #if defined(__BIG_ENDIAN) - u16 reserved0; - u16 protocol_rate; + u16 bd_prod; + u16 cqe_prod; #elif defined(__LITTLE_ENDIAN) - u16 protocol_rate; - u16 reserved0; + u16 cqe_prod; + u16 bd_prod; #endif - u32 protocol_quota; - s32 current_credit; - u32 reserved; -}; - -struct rate_shaping_vars { - struct rate_shaping_per_protocol protocol_vars[NUM_OF_PROTOCOLS]; - u32 pause_mask; - u32 periodic_stop; - u32 rs_periodic_timeout; - u32 rs_threshold; - u32 last_periodic_time; - u32 reserved; -}; - -struct fairness_per_protocol { - u32 credit_delta; - s32 fair_credit; #if defined(__BIG_ENDIAN) - u16 reserved0; - u8 state; - u8 weight; + u16 reserved; + u16 sge_prod; #elif defined(__LITTLE_ENDIAN) - u8 weight; - u8 state; - u16 reserved0; + u16 sge_prod; + u16 reserved; #endif - u32 reserved1; }; -struct fairness_vars { - struct fairness_per_protocol protocol_vars[NUM_OF_PROTOCOLS]; - u32 upper_bound; - u32 port_rate; - u32 pause_mask; - u32 fair_threshold; -}; -struct safc_struct { - u32 cur_pause_mask; - u32 expire_time; +/* + * common flag to indicate existance of TPA. + */ +struct tstorm_eth_tpa_exist { #if defined(__BIG_ENDIAN) - u16 reserved0; - u8 cur_cos_types; - u8 safc_timeout_usec; + u16 reserved1; + u8 reserved0; + u8 tpa_exist; #elif defined(__LITTLE_ENDIAN) - u8 safc_timeout_usec; - u8 cur_cos_types; - u16 reserved0; + u8 tpa_exist; + u8 reserved0; + u16 reserved1; #endif - u32 reserved1; + u32 reserved2; }; -struct demo_struct { + +/* + * per-port SAFC demo variables + */ +struct cmng_flags_per_port { u8 con_number[NUM_OF_PROTOCOLS]; #if defined(__BIG_ENDIAN) - u8 reserved1; u8 fairness_enable; u8 rate_shaping_enable; - u8 cmng_enable; + u8 cmng_protocol_enable; + u8 cmng_vn_enable; #elif defined(__LITTLE_ENDIAN) - u8 cmng_enable; + u8 cmng_vn_enable; + u8 cmng_protocol_enable; u8 rate_shaping_enable; u8 fairness_enable; - u8 reserved1; #endif }; -struct cmng_struct { - struct rate_shaping_vars rs_vars; - struct fairness_vars fair_vars; - struct safc_struct safc_vars; - struct demo_struct demo_vars; + +/* + * per-port rate shaping variables + */ +struct rate_shaping_vars_per_port { + u32 rs_periodic_timeout; + u32 rs_threshold; +}; + + +/* + * per-port fairness variables + */ +struct fairness_vars_per_port { + u32 upper_bound; + u32 fair_threshold; + u32 fairness_timeout; +}; + + +/* + * per-port SAFC variables + */ +struct safc_struct_per_port { +#if defined(__BIG_ENDIAN) + u16 __reserved0; + u8 cur_cos_types; + u8 safc_timeout_usec; +#elif defined(__LITTLE_ENDIAN) + u8 safc_timeout_usec; + u8 cur_cos_types; + u16 __reserved0; +#endif + u8 cos_to_protocol[MAX_COS_NUMBER]; }; -struct cos_to_protocol { - u8 mask[MAX_COS_NUMBER]; +/* + * Per-port congestion management variables + */ +struct cmng_struct_per_port { + struct rate_shaping_vars_per_port rs_vars; + struct fairness_vars_per_port fair_vars; + struct safc_struct_per_port safc_vars; + struct cmng_flags_per_port flags; }; @@ -2161,6 +2423,16 @@ struct eth_stats_query { }; +/* + * per-vnic fairness variables + */ +struct fairness_vars_per_vn { + u32 protocol_credit_delta[NUM_OF_PROTOCOLS]; + u32 vn_credit_delta; + u32 __reserved0; +}; + + /* * FW version stored in the Xstorm RAM */ @@ -2179,8 +2451,10 @@ struct fw_version { #define FW_VERSION_OPTIMIZED_SHIFT 0 #define FW_VERSION_BIG_ENDIEN (0x1<<1) #define FW_VERSION_BIG_ENDIEN_SHIFT 1 -#define __FW_VERSION_RESERVED (0x3FFFFFFF<<2) -#define __FW_VERSION_RESERVED_SHIFT 2 +#define FW_VERSION_CHIP_VERSION (0x3<<2) +#define FW_VERSION_CHIP_VERSION_SHIFT 2 +#define __FW_VERSION_RESERVED (0xFFFFFFF<<4) +#define __FW_VERSION_RESERVED_SHIFT 4 }; @@ -2188,15 +2462,9 @@ struct fw_version { * FW version stored in first line of pram */ struct pram_fw_version { -#if defined(__BIG_ENDIAN) - u16 patch; - u8 primary; - u8 client; -#elif defined(__LITTLE_ENDIAN) u8 client; u8 primary; u16 patch; -#endif u8 flags; #define PRAM_FW_VERSION_OPTIMIZED (0x1<<0) #define PRAM_FW_VERSION_OPTIMIZED_SHIFT 0 @@ -2204,8 +2472,34 @@ struct pram_fw_version { #define PRAM_FW_VERSION_STORM_ID_SHIFT 1 #define PRAM_FW_VERSION_BIG_ENDIEN (0x1<<3) #define PRAM_FW_VERSION_BIG_ENDIEN_SHIFT 3 -#define __PRAM_FW_VERSION_RESERVED0 (0xF<<4) -#define __PRAM_FW_VERSION_RESERVED0_SHIFT 4 +#define PRAM_FW_VERSION_CHIP_VERSION (0x3<<4) +#define PRAM_FW_VERSION_CHIP_VERSION_SHIFT 4 +#define __PRAM_FW_VERSION_RESERVED0 (0x3<<6) +#define __PRAM_FW_VERSION_RESERVED0_SHIFT 6 +}; + + +/* + * a single rate shaping counter. can be used as protocol or vnic counter + */ +struct rate_shaping_counter { + u32 quota; +#if defined(__BIG_ENDIAN) + u16 __reserved0; + u16 rate; +#elif defined(__LITTLE_ENDIAN) + u16 rate; + u16 __reserved0; +#endif +}; + + +/* + * per-vnic rate shaping variables + */ +struct rate_shaping_vars_per_vn { + struct rate_shaping_counter protocol_counters[NUM_OF_PROTOCOLS]; + struct rate_shaping_counter vn_counter; }; diff --git a/drivers/net/bnx2x_init.h b/drivers/net/bnx2x_init.h index 5a4e82b9e7bf..4c7750789b62 100644 --- a/drivers/net/bnx2x_init.h +++ b/drivers/net/bnx2x_init.h @@ -226,28 +226,28 @@ static const u32 *bnx2x_sel_blob(u32 addr, const u32 *data, int is_e1) tsem_int_table_data_e1h; else IF_IS_INT_TABLE_ADDR(CSEM_REG_INT_TABLE, addr) - data = is_e1 ? csem_int_table_data_e1 : - csem_int_table_data_e1h; + data = is_e1 ? csem_int_table_data_e1 : + csem_int_table_data_e1h; else IF_IS_INT_TABLE_ADDR(USEM_REG_INT_TABLE, addr) - data = is_e1 ? usem_int_table_data_e1 : - usem_int_table_data_e1h; + data = is_e1 ? usem_int_table_data_e1 : + usem_int_table_data_e1h; else IF_IS_INT_TABLE_ADDR(XSEM_REG_INT_TABLE, addr) - data = is_e1 ? xsem_int_table_data_e1 : - xsem_int_table_data_e1h; + data = is_e1 ? xsem_int_table_data_e1 : + xsem_int_table_data_e1h; else IF_IS_PRAM_ADDR(TSEM_REG_PRAM, addr) - data = is_e1 ? tsem_pram_data_e1 : tsem_pram_data_e1h; + data = is_e1 ? tsem_pram_data_e1 : tsem_pram_data_e1h; else IF_IS_PRAM_ADDR(CSEM_REG_PRAM, addr) - data = is_e1 ? csem_pram_data_e1 : csem_pram_data_e1h; + data = is_e1 ? csem_pram_data_e1 : csem_pram_data_e1h; else IF_IS_PRAM_ADDR(USEM_REG_PRAM, addr) - data = is_e1 ? usem_pram_data_e1 : usem_pram_data_e1h; + data = is_e1 ? usem_pram_data_e1 : usem_pram_data_e1h; else IF_IS_PRAM_ADDR(XSEM_REG_PRAM, addr) - data = is_e1 ? xsem_pram_data_e1 : xsem_pram_data_e1h; + data = is_e1 ? xsem_pram_data_e1 : xsem_pram_data_e1h; return data; } diff --git a/drivers/net/bnx2x_link.c b/drivers/net/bnx2x_link.c index 22586ebd7b1e..ff2743db10d9 100644 --- a/drivers/net/bnx2x_link.c +++ b/drivers/net/bnx2x_link.c @@ -3572,7 +3572,8 @@ u8 bnx2x_set_led(struct bnx2x *bp, u8 port, u8 mode, u32 speed, LED_BLINK_RATE_VAL); REG_WR(bp, NIG_REG_LED_CONTROL_BLINK_RATE_ENA_P0 + port*4, 1); - if (((speed == SPEED_2500) || + if (!CHIP_IS_E1H(bp) && + ((speed == SPEED_2500) || (speed == SPEED_1000) || (speed == SPEED_100) || (speed == SPEED_10))) { @@ -3753,6 +3754,14 @@ u8 bnx2x_phy_init(struct link_params *params, struct link_vars *vars) vars->duplex = DUPLEX_FULL; vars->flow_ctrl = FLOW_CTRL_NONE; vars->link_status = (LINK_STATUS_LINK_UP | LINK_10GTFD); + /* enable on E1.5 FPGA */ + if (CHIP_IS_E1H(bp)) { + vars->flow_ctrl |= + (FLOW_CTRL_TX | FLOW_CTRL_RX); + vars->link_status |= + (LINK_STATUS_TX_FLOW_CONTROL_ENABLED | + LINK_STATUS_RX_FLOW_CONTROL_ENABLED); + } bnx2x_emac_enable(params, vars, 0); bnx2x_pbf_update(params, vars->flow_ctrl, vars->line_speed); diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index efa942688f84..90b54e4c5c3b 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -1,4 +1,4 @@ -/* bnx2x.c: Broadcom Everest network driver. +/* bnx2x_main.c: Broadcom Everest network driver. * * Copyright (c) 2007-2008 Broadcom Corporation * @@ -15,12 +15,6 @@ * */ -/* define this to make the driver freeze on error - * to allow getting debug info - * (you will need to reboot afterwards) - */ -/*#define BNX2X_STOP_ON_ERROR*/ - #include #include #include @@ -46,16 +40,17 @@ #include #ifdef NETIF_F_HW_VLAN_TX #include - #define BCM_VLAN 1 #endif #include #include #include +#include +#include #include #include +#include #include #include -#include #include #include "bnx2x_reg.h" @@ -67,13 +62,13 @@ #define DRV_MODULE_VERSION "1.42.4" #define DRV_MODULE_RELDATE "2008/4/9" -#define BNX2X_BC_VER 0x040200 +#define BNX2X_BC_VER 0x040200 -/* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT (5*HZ) +/* Time in jiffies before concluding the transmitter is hung */ +#define TX_TIMEOUT (5*HZ) static char version[] __devinitdata = - "Broadcom NetXtreme II 5771X 10Gigabit Ethernet Driver " + "Broadcom NetXtreme II 5771x 10Gigabit Ethernet Driver " DRV_MODULE_NAME " " DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; MODULE_AUTHOR("Eliezer Tamir"); @@ -83,20 +78,19 @@ MODULE_VERSION(DRV_MODULE_VERSION); static int use_inta; static int poll; -static int onefunc; -static int nomcp; static int debug; +static int nomcp; +static int load_count[3]; /* 0-common, 1-port0, 2-port1 */ static int use_multi; module_param(use_inta, int, 0); module_param(poll, int, 0); -module_param(onefunc, int, 0); module_param(debug, int, 0); +module_param(nomcp, int, 0); MODULE_PARM_DESC(use_inta, "use INT#A instead of MSI-X"); MODULE_PARM_DESC(poll, "use polling (for debug)"); -MODULE_PARM_DESC(onefunc, "enable only first function"); -MODULE_PARM_DESC(nomcp, "ignore management CPU (Implies onefunc)"); MODULE_PARM_DESC(debug, "default debug msglevel"); +MODULE_PARM_DESC(nomcp, "ignore management CPU"); #ifdef BNX2X_MULTI module_param(use_multi, int, 0); @@ -105,18 +99,27 @@ MODULE_PARM_DESC(use_multi, "use per-CPU queues"); enum bnx2x_board_type { BCM57710 = 0, + BCM57711 = 1, + BCM57711E = 2, }; -/* indexed by board_t, above */ +/* indexed by board_type, above */ static struct { char *name; } board_info[] __devinitdata = { - { "Broadcom NetXtreme II BCM57710 XGb" } + { "Broadcom NetXtreme II BCM57710 XGb" }, + { "Broadcom NetXtreme II BCM57711 XGb" }, + { "Broadcom NetXtreme II BCM57711E XGb" } }; + static const struct pci_device_id bnx2x_pci_tbl[] = { { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57710, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM57710 }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57711, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM57711 }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57711E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM57711E }, { 0 } }; @@ -201,7 +204,8 @@ void bnx2x_write_dmae(struct bnx2x *bp, dma_addr_t dma_addr, u32 dst_addr, #else DMAE_CMD_ENDIANITY_DW_SWAP | #endif - (bp->port ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0)); + (BP_PORT(bp) ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0) | + (BP_E1HVN(bp) << DMAE_CMD_E1HVN_SHIFT)); dmae->src_addr_lo = U64_LO(dma_addr); dmae->src_addr_hi = U64_HI(dma_addr); dmae->dst_addr_lo = dst_addr >> 2; @@ -224,7 +228,7 @@ void bnx2x_write_dmae(struct bnx2x *bp, dma_addr_t dma_addr, u32 dst_addr, *wb_comp = 0; - bnx2x_post_dmae(bp, dmae, (bp->port)*MAX_DMAE_C_PER_PORT); + bnx2x_post_dmae(bp, dmae, INIT_DMAE_C(bp)); udelay(5); @@ -277,7 +281,8 @@ void bnx2x_read_dmae(struct bnx2x *bp, u32 src_addr, u32 len32) #else DMAE_CMD_ENDIANITY_DW_SWAP | #endif - (bp->port ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0)); + (BP_PORT(bp) ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0) | + (BP_E1HVN(bp) << DMAE_CMD_E1HVN_SHIFT)); dmae->src_addr_lo = src_addr >> 2; dmae->src_addr_hi = 0; dmae->dst_addr_lo = U64_LO(bnx2x_sp_mapping(bp, wb_data)); @@ -297,7 +302,7 @@ void bnx2x_read_dmae(struct bnx2x *bp, u32 src_addr, u32 len32) *wb_comp = 0; - bnx2x_post_dmae(bp, dmae, (bp->port)*MAX_DMAE_C_PER_PORT); + bnx2x_post_dmae(bp, dmae, INIT_DMAE_C(bp)); udelay(5); @@ -345,47 +350,122 @@ static u64 bnx2x_wb_rd(struct bnx2x *bp, int reg) static int bnx2x_mc_assert(struct bnx2x *bp) { - int i, j, rc = 0; char last_idx; - const char storm[] = {"XTCU"}; - const u32 intmem_base[] = { - BAR_XSTRORM_INTMEM, - BAR_TSTRORM_INTMEM, - BAR_CSTRORM_INTMEM, - BAR_USTRORM_INTMEM - }; - - /* Go through all instances of all SEMIs */ - for (i = 0; i < 4; i++) { - last_idx = REG_RD8(bp, XSTORM_ASSERT_LIST_INDEX_OFFSET + - intmem_base[i]); - if (last_idx) - BNX2X_LOG("DATA %cSTORM_ASSERT_LIST_INDEX 0x%x\n", - storm[i], last_idx); - - /* print the asserts */ - for (j = 0; j < STROM_ASSERT_ARRAY_SIZE; j++) { - u32 row0, row1, row2, row3; - - row0 = REG_RD(bp, XSTORM_ASSERT_LIST_OFFSET(j) + - intmem_base[i]); - row1 = REG_RD(bp, XSTORM_ASSERT_LIST_OFFSET(j) + 4 + - intmem_base[i]); - row2 = REG_RD(bp, XSTORM_ASSERT_LIST_OFFSET(j) + 8 + - intmem_base[i]); - row3 = REG_RD(bp, XSTORM_ASSERT_LIST_OFFSET(j) + 12 + - intmem_base[i]); - - if (row0 != COMMON_ASM_INVALID_ASSERT_OPCODE) { - BNX2X_LOG("DATA %cSTORM_ASSERT_INDEX 0x%x =" - " 0x%08x 0x%08x 0x%08x 0x%08x\n", - storm[i], j, row3, row2, row1, row0); - rc++; - } else { - break; - } + int i, rc = 0; + u32 row0, row1, row2, row3; + + /* XSTORM */ + last_idx = REG_RD8(bp, BAR_XSTRORM_INTMEM + + XSTORM_ASSERT_LIST_INDEX_OFFSET); + if (last_idx) + BNX2X_ERR("XSTORM_ASSERT_LIST_INDEX 0x%x\n", last_idx); + + /* print the asserts */ + for (i = 0; i < STROM_ASSERT_ARRAY_SIZE; i++) { + + row0 = REG_RD(bp, BAR_XSTRORM_INTMEM + + XSTORM_ASSERT_LIST_OFFSET(i)); + row1 = REG_RD(bp, BAR_XSTRORM_INTMEM + + XSTORM_ASSERT_LIST_OFFSET(i) + 4); + row2 = REG_RD(bp, BAR_XSTRORM_INTMEM + + XSTORM_ASSERT_LIST_OFFSET(i) + 8); + row3 = REG_RD(bp, BAR_XSTRORM_INTMEM + + XSTORM_ASSERT_LIST_OFFSET(i) + 12); + + if (row0 != COMMON_ASM_INVALID_ASSERT_OPCODE) { + BNX2X_ERR("XSTORM_ASSERT_INDEX 0x%x = 0x%08x" + " 0x%08x 0x%08x 0x%08x\n", + i, row3, row2, row1, row0); + rc++; + } else { + break; + } + } + + /* TSTORM */ + last_idx = REG_RD8(bp, BAR_TSTRORM_INTMEM + + TSTORM_ASSERT_LIST_INDEX_OFFSET); + if (last_idx) + BNX2X_ERR("TSTORM_ASSERT_LIST_INDEX 0x%x\n", last_idx); + + /* print the asserts */ + for (i = 0; i < STROM_ASSERT_ARRAY_SIZE; i++) { + + row0 = REG_RD(bp, BAR_TSTRORM_INTMEM + + TSTORM_ASSERT_LIST_OFFSET(i)); + row1 = REG_RD(bp, BAR_TSTRORM_INTMEM + + TSTORM_ASSERT_LIST_OFFSET(i) + 4); + row2 = REG_RD(bp, BAR_TSTRORM_INTMEM + + TSTORM_ASSERT_LIST_OFFSET(i) + 8); + row3 = REG_RD(bp, BAR_TSTRORM_INTMEM + + TSTORM_ASSERT_LIST_OFFSET(i) + 12); + + if (row0 != COMMON_ASM_INVALID_ASSERT_OPCODE) { + BNX2X_ERR("TSTORM_ASSERT_INDEX 0x%x = 0x%08x" + " 0x%08x 0x%08x 0x%08x\n", + i, row3, row2, row1, row0); + rc++; + } else { + break; + } + } + + /* CSTORM */ + last_idx = REG_RD8(bp, BAR_CSTRORM_INTMEM + + CSTORM_ASSERT_LIST_INDEX_OFFSET); + if (last_idx) + BNX2X_ERR("CSTORM_ASSERT_LIST_INDEX 0x%x\n", last_idx); + + /* print the asserts */ + for (i = 0; i < STROM_ASSERT_ARRAY_SIZE; i++) { + + row0 = REG_RD(bp, BAR_CSTRORM_INTMEM + + CSTORM_ASSERT_LIST_OFFSET(i)); + row1 = REG_RD(bp, BAR_CSTRORM_INTMEM + + CSTORM_ASSERT_LIST_OFFSET(i) + 4); + row2 = REG_RD(bp, BAR_CSTRORM_INTMEM + + CSTORM_ASSERT_LIST_OFFSET(i) + 8); + row3 = REG_RD(bp, BAR_CSTRORM_INTMEM + + CSTORM_ASSERT_LIST_OFFSET(i) + 12); + + if (row0 != COMMON_ASM_INVALID_ASSERT_OPCODE) { + BNX2X_ERR("CSTORM_ASSERT_INDEX 0x%x = 0x%08x" + " 0x%08x 0x%08x 0x%08x\n", + i, row3, row2, row1, row0); + rc++; + } else { + break; + } + } + + /* USTORM */ + last_idx = REG_RD8(bp, BAR_USTRORM_INTMEM + + USTORM_ASSERT_LIST_INDEX_OFFSET); + if (last_idx) + BNX2X_ERR("USTORM_ASSERT_LIST_INDEX 0x%x\n", last_idx); + + /* print the asserts */ + for (i = 0; i < STROM_ASSERT_ARRAY_SIZE; i++) { + + row0 = REG_RD(bp, BAR_USTRORM_INTMEM + + USTORM_ASSERT_LIST_OFFSET(i)); + row1 = REG_RD(bp, BAR_USTRORM_INTMEM + + USTORM_ASSERT_LIST_OFFSET(i) + 4); + row2 = REG_RD(bp, BAR_USTRORM_INTMEM + + USTORM_ASSERT_LIST_OFFSET(i) + 8); + row3 = REG_RD(bp, BAR_USTRORM_INTMEM + + USTORM_ASSERT_LIST_OFFSET(i) + 12); + + if (row0 != COMMON_ASM_INVALID_ASSERT_OPCODE) { + BNX2X_ERR("USTORM_ASSERT_INDEX 0x%x = 0x%08x" + " 0x%08x 0x%08x 0x%08x\n", + i, row3, row2, row1, row0); + rc++; + } else { + break; } } + return rc; } @@ -428,14 +508,16 @@ static void bnx2x_panic_dump(struct bnx2x *bp) struct eth_tx_db_data *hw_prods = fp->hw_tx_prods; BNX2X_ERR("queue[%d]: tx_pkt_prod(%x) tx_pkt_cons(%x)" - " tx_bd_prod(%x) tx_bd_cons(%x) *tx_cons_sb(%x)" - " *rx_cons_sb(%x) rx_comp_prod(%x)" - " rx_comp_cons(%x) fp_c_idx(%x) fp_u_idx(%x)" - " bd data(%x,%x)\n", + " tx_bd_prod(%x) tx_bd_cons(%x) *tx_cons_sb(%x)\n", i, fp->tx_pkt_prod, fp->tx_pkt_cons, fp->tx_bd_prod, - fp->tx_bd_cons, *fp->tx_cons_sb, *fp->rx_cons_sb, - fp->rx_comp_prod, fp->rx_comp_cons, fp->fp_c_idx, - fp->fp_u_idx, hw_prods->packets_prod, + fp->tx_bd_cons, le16_to_cpu(*fp->tx_cons_sb)); + BNX2X_ERR(" rx_comp_prod(%x) rx_comp_cons(%x)" + " *rx_cons_sb(%x)\n", + fp->rx_comp_prod, fp->rx_comp_cons, + le16_to_cpu(*fp->rx_cons_sb)); + BNX2X_ERR(" fp_c_idx(%x) fp_u_idx(%x)" + " bd data(%x,%x)\n", + fp->fp_c_idx, fp->fp_u_idx, hw_prods->packets_prod, hw_prods->bds_prod); start = TX_BD(le16_to_cpu(*fp->tx_cons_sb) - 10); @@ -463,7 +545,7 @@ static void bnx2x_panic_dump(struct bnx2x *bp) struct sw_rx_bd *sw_bd = &fp->rx_buf_ring[j]; BNX2X_ERR("rx_bd[%x]=[%x:%x] sw_bd=[%p]\n", - j, rx_bd[0], rx_bd[1], sw_bd->skb); + j, rx_bd[1], rx_bd[0], sw_bd->skb); } start = RCQ_BD(fp->rx_comp_cons - 10); @@ -482,7 +564,7 @@ static void bnx2x_panic_dump(struct bnx2x *bp) bp->def_c_idx, bp->def_u_idx, bp->def_x_idx, bp->def_t_idx, bp->def_att_idx, bp->attn_state, bp->spq_prod_idx); - + bnx2x_fw_dump(bp); bnx2x_mc_assert(bp); BNX2X_ERR("end crash dump -----------------\n"); @@ -492,7 +574,7 @@ static void bnx2x_panic_dump(struct bnx2x *bp) static void bnx2x_int_enable(struct bnx2x *bp) { - int port = bp->port; + int port = BP_PORT(bp); u32 addr = port ? HC_REG_CONFIG_1 : HC_REG_CONFIG_0; u32 val = REG_RD(bp, addr); int msix = (bp->flags & USING_MSIX_FLAG) ? 1 : 0; @@ -507,7 +589,6 @@ static void bnx2x_int_enable(struct bnx2x *bp) HC_CONFIG_0_REG_INT_LINE_EN_0 | HC_CONFIG_0_REG_ATTN_BIT_EN_0); - /* Errata A0.158 workaround */ DP(NETIF_MSG_INTR, "write %x to HC %d (addr 0x%x) MSI-X %d\n", val, port, addr, msix); @@ -520,11 +601,25 @@ static void bnx2x_int_enable(struct bnx2x *bp) val, port, addr, msix); REG_WR(bp, addr, val); + + if (CHIP_IS_E1H(bp)) { + /* init leading/trailing edge */ + if (IS_E1HMF(bp)) { + val = (0xfe0f | (1 << (BP_E1HVN(bp) + 4))); + if (bp->port.pmf) + /* enable nig attention */ + val |= 0x0100; + } else + val = 0xffff; + + REG_WR(bp, HC_REG_TRAILING_EDGE_0 + port*8, val); + REG_WR(bp, HC_REG_LEADING_EDGE_0 + port*8, val); + } } static void bnx2x_int_disable(struct bnx2x *bp) { - int port = bp->port; + int port = BP_PORT(bp); u32 addr = port ? HC_REG_CONFIG_1 : HC_REG_CONFIG_0; u32 val = REG_RD(bp, addr); @@ -543,10 +638,10 @@ static void bnx2x_int_disable(struct bnx2x *bp) static void bnx2x_int_disable_sync(struct bnx2x *bp) { - int msix = (bp->flags & USING_MSIX_FLAG) ? 1 : 0; int i; + /* disable interrupt handling */ atomic_inc(&bp->intr_sem); /* prevent the HW from sending interrupts */ bnx2x_int_disable(bp); @@ -563,30 +658,29 @@ static void bnx2x_int_disable_sync(struct bnx2x *bp) /* make sure sp_task is not running */ cancel_work_sync(&bp->sp_task); - } -/* fast path code */ +/* fast path */ /* - * general service functions + * General service functions */ -static inline void bnx2x_ack_sb(struct bnx2x *bp, u8 id, +static inline void bnx2x_ack_sb(struct bnx2x *bp, u8 sb_id, u8 storm, u16 index, u8 op, u8 update) { - u32 igu_addr = (IGU_ADDR_INT_ACK + IGU_PORT_BASE * bp->port) * 8; + u32 igu_addr = (IGU_ADDR_INT_ACK + IGU_FUNC_BASE * BP_FUNC(bp)) * 8; struct igu_ack_register igu_ack; igu_ack.status_block_index = index; igu_ack.sb_id_and_flags = - ((id << IGU_ACK_REGISTER_STATUS_BLOCK_ID_SHIFT) | + ((sb_id << IGU_ACK_REGISTER_STATUS_BLOCK_ID_SHIFT) | (storm << IGU_ACK_REGISTER_STORM_ID_SHIFT) | (update << IGU_ACK_REGISTER_UPDATE_INDEX_SHIFT) | (op << IGU_ACK_REGISTER_INTERRUPT_MODE_SHIFT)); -/* DP(NETIF_MSG_INTR, "write 0x%08x to IGU addr 0x%x\n", - (*(u32 *)&igu_ack), BAR_IGU_INTMEM + igu_addr); */ + DP(BNX2X_MSG_OFF, "write 0x%08x to IGU addr 0x%x\n", + (*(u32 *)&igu_ack), BAR_IGU_INTMEM + igu_addr); REG_WR(bp, BAR_IGU_INTMEM + igu_addr, (*(u32 *)&igu_ack)); } @@ -614,8 +708,9 @@ static inline int bnx2x_has_work(struct bnx2x_fastpath *fp) if ((rx_cons_sb & MAX_RCQ_DESC_CNT) == MAX_RCQ_DESC_CNT) rx_cons_sb++; - if ((rx_cons_sb != fp->rx_comp_cons) || - (le16_to_cpu(*fp->tx_cons_sb) != fp->tx_pkt_cons)) + if ((fp->rx_comp_cons != rx_cons_sb) || + (fp->tx_pkt_prod != le16_to_cpu(*fp->tx_cons_sb)) || + (fp->tx_pkt_prod != fp->tx_pkt_cons)) return 1; return 0; @@ -623,11 +718,11 @@ static inline int bnx2x_has_work(struct bnx2x_fastpath *fp) static u16 bnx2x_ack_int(struct bnx2x *bp) { - u32 igu_addr = (IGU_ADDR_SIMD_MASK + IGU_PORT_BASE * bp->port) * 8; + u32 igu_addr = (IGU_ADDR_SIMD_MASK + IGU_FUNC_BASE * BP_FUNC(bp)) * 8; u32 result = REG_RD(bp, BAR_IGU_INTMEM + igu_addr); -/* DP(NETIF_MSG_INTR, "read 0x%08x from IGU addr 0x%x\n", - result, BAR_IGU_INTMEM + igu_addr); */ + DP(BNX2X_MSG_OFF, "read 0x%08x from IGU addr 0x%x\n", + result, BAR_IGU_INTMEM + igu_addr); #ifdef IGU_DEBUG #warning IGU_DEBUG active @@ -653,7 +748,7 @@ static u16 bnx2x_free_tx_pkt(struct bnx2x *bp, struct bnx2x_fastpath *fp, struct sw_tx_bd *tx_buf = &fp->tx_buf_ring[idx]; struct eth_tx_bd *tx_bd; struct sk_buff *skb = tx_buf->skb; - u16 bd_idx = tx_buf->first_bd; + u16 bd_idx = TX_BD(tx_buf->first_bd), new_cons; int nbd; DP(BNX2X_MSG_OFF, "pkt_idx %d buff @(%p)->skb %p\n", @@ -666,9 +761,10 @@ static u16 bnx2x_free_tx_pkt(struct bnx2x *bp, struct bnx2x_fastpath *fp, BD_UNMAP_LEN(tx_bd), PCI_DMA_TODEVICE); nbd = le16_to_cpu(tx_bd->nbd) - 1; + new_cons = nbd + tx_buf->first_bd; #ifdef BNX2X_STOP_ON_ERROR if (nbd > (MAX_SKB_FRAGS + 2)) { - BNX2X_ERR("bad nbd!\n"); + BNX2X_ERR("BAD nbd!\n"); bnx2x_panic(); } #endif @@ -708,32 +804,30 @@ static u16 bnx2x_free_tx_pkt(struct bnx2x *bp, struct bnx2x_fastpath *fp, tx_buf->first_bd = 0; tx_buf->skb = NULL; - return bd_idx; + return new_cons; } -static inline u32 bnx2x_tx_avail(struct bnx2x_fastpath *fp) +static inline u16 bnx2x_tx_avail(struct bnx2x_fastpath *fp) { - u16 used; - u32 prod; - u32 cons; + s16 used; + u16 prod; + u16 cons; - /* Tell compiler that prod and cons can change */ - barrier(); + barrier(); /* Tell compiler that prod and cons can change */ prod = fp->tx_bd_prod; cons = fp->tx_bd_cons; - used = (NUM_TX_BD - NUM_TX_RINGS + prod - cons + - (cons / TX_DESC_CNT) - (prod / TX_DESC_CNT)); - - if (prod >= cons) { - /* used = prod - cons - prod/size + cons/size */ - used -= NUM_TX_BD - NUM_TX_RINGS; - } + /* NUM_TX_RINGS = number of "next-page" entries + It will be used as a threshold */ + used = SUB_S16(prod, cons) + (s16)NUM_TX_RINGS; +#ifdef BNX2X_STOP_ON_ERROR + BUG_TRAP(used >= 0); BUG_TRAP(used <= fp->bp->tx_ring_size); BUG_TRAP((fp->bp->tx_ring_size - used) <= MAX_TX_AVAIL); +#endif - return (fp->bp->tx_ring_size - used); + return (s16)(fp->bp->tx_ring_size) - used; } static void bnx2x_tx_int(struct bnx2x_fastpath *fp, int work) @@ -757,10 +851,10 @@ static void bnx2x_tx_int(struct bnx2x_fastpath *fp, int work) /* prefetch(bp->tx_buf_ring[pkt_cons].skb); */ - DP(NETIF_MSG_TX_DONE, "hw_cons %u sw_cons %u pkt_cons %d\n", + DP(NETIF_MSG_TX_DONE, "hw_cons %u sw_cons %u pkt_cons %u\n", hw_cons, sw_cons, pkt_cons); -/* if (NEXT_TX_IDX(sw_cons) != hw_cons) { +/* if (NEXT_TX_IDX(sw_cons) != hw_cons) { rmb(); prefetch(fp->tx_buf_ring[NEXT_TX_IDX(sw_cons)].skb); } @@ -793,7 +887,6 @@ static void bnx2x_tx_int(struct bnx2x_fastpath *fp, int work) netif_wake_queue(bp->dev); netif_tx_unlock(bp->dev); - } } @@ -804,13 +897,14 @@ static void bnx2x_sp_event(struct bnx2x_fastpath *fp, int cid = SW_CID(rr_cqe->ramrod_cqe.conn_and_cmd_data); int command = CQE_CMD(rr_cqe->ramrod_cqe.conn_and_cmd_data); - DP(NETIF_MSG_RX_STATUS, + DP(BNX2X_MSG_SP, "fp %d cid %d got ramrod #%d state is %x type is %d\n", - fp->index, cid, command, bp->state, rr_cqe->ramrod_cqe.type); + FP_IDX(fp), cid, command, bp->state, + rr_cqe->ramrod_cqe.ramrod_type); bp->spq_left++; - if (fp->index) { + if (FP_IDX(fp)) { switch (command | fp->state) { case (RAMROD_CMD_ID_ETH_CLIENT_SETUP | BNX2X_FP_STATE_OPENING): @@ -826,10 +920,11 @@ static void bnx2x_sp_event(struct bnx2x_fastpath *fp, break; default: - BNX2X_ERR("unexpected MC reply(%d) state is %x\n", - command, fp->state); + BNX2X_ERR("unexpected MC reply (%d) " + "fp->state is %x\n", command, fp->state); + break; } - mb(); /* force bnx2x_wait_ramrod to see the change */ + mb(); /* force bnx2x_wait_ramrod() to see the change */ return; } @@ -846,25 +941,25 @@ static void bnx2x_sp_event(struct bnx2x_fastpath *fp, break; case (RAMROD_CMD_ID_ETH_CFC_DEL | BNX2X_STATE_CLOSING_WAIT4_HALT): - DP(NETIF_MSG_IFDOWN, "got delete ramrod for MULTI[%d]\n", - cid); + DP(NETIF_MSG_IFDOWN, "got delete ramrod for MULTI[%d]\n", cid); bnx2x_fp(bp, cid, state) = BNX2X_FP_STATE_CLOSED; break; case (RAMROD_CMD_ID_ETH_SET_MAC | BNX2X_STATE_OPEN): + case (RAMROD_CMD_ID_ETH_SET_MAC | BNX2X_STATE_DIAG): DP(NETIF_MSG_IFUP, "got set mac ramrod\n"); break; case (RAMROD_CMD_ID_ETH_SET_MAC | BNX2X_STATE_CLOSING_WAIT4_HALT): - DP(NETIF_MSG_IFUP, "got (un)set mac ramrod\n"); + DP(NETIF_MSG_IFDOWN, "got (un)set mac ramrod\n"); break; default: - BNX2X_ERR("unexpected ramrod (%d) state is %x\n", + BNX2X_ERR("unexpected MC reply (%d) bp->state is %x\n", command, bp->state); + break; } - - mb(); /* force bnx2x_wait_ramrod to see the change */ + mb(); /* force bnx2x_wait_ramrod() to see the change */ } static inline int bnx2x_alloc_rx_skb(struct bnx2x *bp, @@ -882,7 +977,6 @@ static inline int bnx2x_alloc_rx_skb(struct bnx2x *bp, mapping = pci_map_single(bp->pdev, skb->data, bp->rx_buf_use_size, PCI_DMA_FROMDEVICE); if (unlikely(dma_mapping_error(mapping))) { - dev_kfree_skb(skb); return -ENOMEM; } @@ -924,7 +1018,7 @@ static void bnx2x_reuse_rx_skb(struct bnx2x_fastpath *fp, static int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget) { struct bnx2x *bp = fp->bp; - u16 bd_cons, bd_prod, comp_ring_cons; + u16 bd_cons, bd_prod, bd_prod_fw, comp_ring_cons; u16 hw_comp_cons, sw_comp_cons, sw_comp_prod; int rx_pkt = 0; @@ -933,12 +1027,15 @@ static int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget) return 0; #endif + /* CQ "next element" is of the size of the regular element, + that's why it's ok here */ hw_comp_cons = le16_to_cpu(*fp->rx_cons_sb); if ((hw_comp_cons & MAX_RCQ_DESC_CNT) == MAX_RCQ_DESC_CNT) hw_comp_cons++; bd_cons = fp->rx_bd_cons; bd_prod = fp->rx_bd_prod; + bd_prod_fw = bd_prod; sw_comp_cons = fp->rx_comp_cons; sw_comp_prod = fp->rx_comp_prod; @@ -949,34 +1046,31 @@ static int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget) DP(NETIF_MSG_RX_STATUS, "queue[%d]: hw_comp_cons %u sw_comp_cons %u\n", - fp->index, hw_comp_cons, sw_comp_cons); + FP_IDX(fp), hw_comp_cons, sw_comp_cons); while (sw_comp_cons != hw_comp_cons) { - unsigned int len, pad; - struct sw_rx_bd *rx_buf; + struct sw_rx_bd *rx_buf = NULL; struct sk_buff *skb; union eth_rx_cqe *cqe; + u8 cqe_fp_flags; + u16 len, pad; comp_ring_cons = RCQ_BD(sw_comp_cons); bd_prod = RX_BD(bd_prod); bd_cons = RX_BD(bd_cons); cqe = &fp->rx_comp_ring[comp_ring_cons]; + cqe_fp_flags = cqe->fast_path_cqe.type_error_flags; - DP(NETIF_MSG_RX_STATUS, "hw_comp_cons %u sw_comp_cons %u" - " comp_ring (%u) bd_ring (%u,%u)\n", - hw_comp_cons, sw_comp_cons, - comp_ring_cons, bd_prod, bd_cons); DP(NETIF_MSG_RX_STATUS, "CQE type %x err %x status %x" - " queue %x vlan %x len %x\n", - cqe->fast_path_cqe.type, - cqe->fast_path_cqe.error_type_flags, - cqe->fast_path_cqe.status_flags, + " queue %x vlan %x len %u\n", CQE_TYPE(cqe_fp_flags), + cqe_fp_flags, cqe->fast_path_cqe.status_flags, cqe->fast_path_cqe.rss_hash_result, - cqe->fast_path_cqe.vlan_tag, cqe->fast_path_cqe.pkt_len); + le16_to_cpu(cqe->fast_path_cqe.vlan_tag), + le16_to_cpu(cqe->fast_path_cqe.pkt_len)); /* is this a slowpath msg? */ - if (unlikely(cqe->fast_path_cqe.type)) { + if (unlikely(CQE_TYPE(cqe_fp_flags))) { bnx2x_sp_event(fp, cqe); goto next_cqe; @@ -984,7 +1078,6 @@ static int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget) } else { rx_buf = &fp->rx_buf_ring[bd_cons]; skb = rx_buf->skb; - len = le16_to_cpu(cqe->fast_path_cqe.pkt_len); pad = cqe->fast_path_cqe.placement_offset; @@ -996,13 +1089,11 @@ static int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget) prefetch(((char *)(skb)) + 128); /* is this an error packet? */ - if (unlikely(cqe->fast_path_cqe.error_type_flags & - ETH_RX_ERROR_FALGS)) { + if (unlikely(cqe_fp_flags & ETH_RX_ERROR_FALGS)) { /* do we sometimes forward error packets anyway? */ DP(NETIF_MSG_RX_ERR, - "ERROR flags(%u) Rx packet(%u)\n", - cqe->fast_path_cqe.error_type_flags, - sw_comp_cons); + "ERROR flags %x rx packet %u\n", + cqe_fp_flags, sw_comp_cons); /* TBD make sure MC counts this as a drop */ goto reuse_rx; } @@ -1018,7 +1109,7 @@ static int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget) len + pad); if (new_skb == NULL) { DP(NETIF_MSG_RX_ERR, - "ERROR packet dropped " + "ERROR packet dropped " "because of alloc failure\n"); /* TBD count this as a drop? */ goto reuse_rx; @@ -1044,7 +1135,7 @@ static int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget) } else { DP(NETIF_MSG_RX_ERR, - "ERROR packet dropped because " + "ERROR packet dropped because " "of alloc failure\n"); reuse_rx: bnx2x_reuse_rx_skb(fp, skb, bd_cons, bd_prod); @@ -1061,14 +1152,14 @@ reuse_rx: } #ifdef BCM_VLAN - if ((le16_to_cpu(cqe->fast_path_cqe.pars_flags.flags) - & PARSING_FLAGS_NUMBER_OF_NESTED_VLANS) - && (bp->vlgrp != NULL)) + if ((bp->vlgrp != NULL) && + (le16_to_cpu(cqe->fast_path_cqe.pars_flags.flags) & + PARSING_FLAGS_VLAN)) vlan_hwaccel_receive_skb(skb, bp->vlgrp, le16_to_cpu(cqe->fast_path_cqe.vlan_tag)); else #endif - netif_receive_skb(skb); + netif_receive_skb(skb); bp->dev->last_rx = jiffies; @@ -1077,22 +1168,25 @@ next_rx: bd_cons = NEXT_RX_IDX(bd_cons); bd_prod = NEXT_RX_IDX(bd_prod); + bd_prod_fw = NEXT_RX_IDX(bd_prod_fw); + rx_pkt++; next_cqe: sw_comp_prod = NEXT_RCQ_IDX(sw_comp_prod); sw_comp_cons = NEXT_RCQ_IDX(sw_comp_cons); - rx_pkt++; - if ((rx_pkt == budget)) + if (rx_pkt == budget) break; } /* while */ fp->rx_bd_cons = bd_cons; - fp->rx_bd_prod = bd_prod; + fp->rx_bd_prod = bd_prod_fw; fp->rx_comp_cons = sw_comp_cons; fp->rx_comp_prod = sw_comp_prod; REG_WR(bp, BAR_TSTRORM_INTMEM + - TSTORM_RCQ_PROD_OFFSET(bp->port, fp->index), sw_comp_prod); + TSTORM_RX_PRODS_OFFSET(BP_PORT(bp), FP_CL_ID(fp)), + sw_comp_prod); + mmiowb(); /* keep prod updates ordered */ @@ -1107,10 +1201,11 @@ static irqreturn_t bnx2x_msix_fp_int(int irq, void *fp_cookie) struct bnx2x_fastpath *fp = fp_cookie; struct bnx2x *bp = fp->bp; struct net_device *dev = bp->dev; - int index = fp->index; + int index = FP_IDX(fp); - DP(NETIF_MSG_INTR, "got an msix interrupt on [%d]\n", index); - bnx2x_ack_sb(bp, index, USTORM_ID, 0, IGU_INT_DISABLE, 0); + DP(BNX2X_MSG_FP, "got an MSI-X interrupt on IDX:SB [%d:%d]\n", + index, FP_SB_ID(fp)); + bnx2x_ack_sb(bp, FP_SB_ID(fp), USTORM_ID, 0, IGU_INT_DISABLE, 0); #ifdef BNX2X_STOP_ON_ERROR if (unlikely(bp->panic)) @@ -1123,6 +1218,7 @@ static irqreturn_t bnx2x_msix_fp_int(int irq, void *fp_cookie) prefetch(&fp->status_blk->u_status_block.status_block_index); netif_rx_schedule(dev, &bnx2x_fp(bp, index, napi)); + return IRQ_HANDLED; } @@ -1131,26 +1227,28 @@ static irqreturn_t bnx2x_interrupt(int irq, void *dev_instance) struct net_device *dev = dev_instance; struct bnx2x *bp = netdev_priv(dev); u16 status = bnx2x_ack_int(bp); + u16 mask; + /* Return here if interrupt is shared and it's not for us */ if (unlikely(status == 0)) { DP(NETIF_MSG_INTR, "not our interrupt!\n"); return IRQ_NONE; } - - DP(NETIF_MSG_INTR, "got an interrupt status is %u\n", status); + DP(NETIF_MSG_INTR, "got an interrupt status %u\n", status); #ifdef BNX2X_STOP_ON_ERROR if (unlikely(bp->panic)) return IRQ_HANDLED; #endif - /* Return here if interrupt is shared and is disabled */ + /* Return here if interrupt is disabled */ if (unlikely(atomic_read(&bp->intr_sem) != 0)) { DP(NETIF_MSG_INTR, "called but intr_sem not 0, returning\n"); return IRQ_HANDLED; } - if (status & 0x2) { + mask = 0x2 << bp->fp[0].sb_id; + if (status & mask) { struct bnx2x_fastpath *fp = &bp->fp[0]; prefetch(fp->rx_cons_sb); @@ -1160,13 +1258,11 @@ static irqreturn_t bnx2x_interrupt(int irq, void *dev_instance) netif_rx_schedule(dev, &bnx2x_fp(bp, 0, napi)); - status &= ~0x2; - if (!status) - return IRQ_HANDLED; + status &= ~mask; } - if (unlikely(status & 0x1)) { + if (unlikely(status & 0x1)) { schedule_work(&bp->sp_task); status &= ~0x1; @@ -1174,8 +1270,9 @@ static irqreturn_t bnx2x_interrupt(int irq, void *dev_instance) return IRQ_HANDLED; } - DP(NETIF_MSG_INTR, "got an unknown interrupt! (status is %u)\n", - status); + if (status) + DP(NETIF_MSG_INTR, "got an unknown interrupt! (status %u)\n", + status); return IRQ_HANDLED; } @@ -1193,7 +1290,7 @@ static int bnx2x_hw_lock(struct bnx2x *bp, u32 resource) { u32 lock_status; u32 resource_bit = (1 << resource); - u8 port = bp->port; + u8 port = BP_PORT(bp); int cnt; /* Validating that the resource is within range */ @@ -1231,7 +1328,7 @@ static int bnx2x_hw_unlock(struct bnx2x *bp, u32 resource) { u32 lock_status; u32 resource_bit = (1 << resource); - u8 port = bp->port; + u8 port = BP_PORT(bp); /* Validating that the resource is within range */ if (resource > HW_LOCK_MAX_RESOURCE_VALUE) { @@ -1258,7 +1355,7 @@ static void bnx2x_phy_hw_lock(struct bnx2x *bp) { u32 ext_phy_type = XGXS_EXT_PHY_TYPE(bp->link_params.ext_phy_config); - mutex_lock(&bp->phy_mutex); + mutex_lock(&bp->port.phy_mutex); if ((ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072) || (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073)) @@ -1273,14 +1370,14 @@ static void bnx2x_phy_hw_unlock(struct bnx2x *bp) (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073)) bnx2x_hw_unlock(bp, HW_LOCK_RESOURCE_8072_MDIO); - mutex_unlock(&bp->phy_mutex); + mutex_unlock(&bp->port.phy_mutex); } int bnx2x_set_gpio(struct bnx2x *bp, int gpio_num, u32 mode) { /* The GPIO should be swapped if swap register is set and active */ int gpio_port = (REG_RD(bp, NIG_REG_PORT_SWAP) && - REG_RD(bp, NIG_REG_STRAP_OVERRIDE)) ^ bp->port; + REG_RD(bp, NIG_REG_STRAP_OVERRIDE)) ^ BP_PORT(bp); int gpio_shift = gpio_num + (gpio_port ? MISC_REGISTERS_GPIO_PORT_SHIFT : 0); u32 gpio_mask = (1 << gpio_shift); @@ -1379,18 +1476,18 @@ static void bnx2x_calc_fc_adv(struct bnx2x *bp) { switch (bp->link_vars.ieee_fc) { case MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_NONE: - bp->advertising &= ~(ADVERTISED_Asym_Pause | + bp->port.advertising &= ~(ADVERTISED_Asym_Pause | ADVERTISED_Pause); break; case MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH: - bp->advertising |= (ADVERTISED_Asym_Pause | + bp->port.advertising |= (ADVERTISED_Asym_Pause | ADVERTISED_Pause); break; case MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC: - bp->advertising |= ADVERTISED_Asym_Pause; + bp->port.advertising |= ADVERTISED_Asym_Pause; break; default: - bp->advertising &= ~(ADVERTISED_Asym_Pause | + bp->port.advertising &= ~(ADVERTISED_Asym_Pause | ADVERTISED_Pause); break; } @@ -1443,6 +1540,7 @@ static u8 bnx2x_initial_phy_init(struct bnx2x *bp) bnx2x_link_report(bp); bnx2x_calc_fc_adv(bp); + return rc; } @@ -1473,15 +1571,261 @@ static u8 bnx2x_link_test(struct bnx2x *bp) return rc; } +/* Calculates the sum of vn_min_rates. + It's needed for further normalizing of the min_rates. + + Returns: + sum of vn_min_rates + or + 0 - if all the min_rates are 0. + In the later case fainess algorithm should be deactivated. + If not all min_rates are zero then those that are zeroes will + be set to 1. + */ +static u32 bnx2x_calc_vn_wsum(struct bnx2x *bp) +{ + int i, port = BP_PORT(bp); + u32 wsum = 0; + int all_zero = 1; + + for (i = 0; i < E1HVN_MAX; i++) { + u32 vn_cfg = + SHMEM_RD(bp, mf_cfg.func_mf_config[2*i + port].config); + u32 vn_min_rate = ((vn_cfg & FUNC_MF_CFG_MIN_BW_MASK) >> + FUNC_MF_CFG_MIN_BW_SHIFT) * 100; + if (!(vn_cfg & FUNC_MF_CFG_FUNC_HIDE)) { + /* If min rate is zero - set it to 1 */ + if (!vn_min_rate) + vn_min_rate = DEF_MIN_RATE; + else + all_zero = 0; + + wsum += vn_min_rate; + } + } + + /* ... only if all min rates are zeros - disable FAIRNESS */ + if (all_zero) + return 0; + + return wsum; +} + +static void bnx2x_init_port_minmax(struct bnx2x *bp, + int en_fness, + u16 port_rate, + struct cmng_struct_per_port *m_cmng_port) +{ + u32 r_param = port_rate / 8; + int port = BP_PORT(bp); + int i; + + memset(m_cmng_port, 0, sizeof(struct cmng_struct_per_port)); + + /* Enable minmax only if we are in e1hmf mode */ + if (IS_E1HMF(bp)) { + u32 fair_periodic_timeout_usec; + u32 t_fair; + + /* Enable rate shaping and fairness */ + m_cmng_port->flags.cmng_vn_enable = 1; + m_cmng_port->flags.fairness_enable = en_fness ? 1 : 0; + m_cmng_port->flags.rate_shaping_enable = 1; + + if (!en_fness) + DP(NETIF_MSG_IFUP, "All MIN values are zeroes" + " fairness will be disabled\n"); + + /* 100 usec in SDM ticks = 25 since each tick is 4 usec */ + m_cmng_port->rs_vars.rs_periodic_timeout = + RS_PERIODIC_TIMEOUT_USEC / 4; + + /* this is the threshold below which no timer arming will occur + 1.25 coefficient is for the threshold to be a little bigger + than the real time, to compensate for timer in-accuracy */ + m_cmng_port->rs_vars.rs_threshold = + (RS_PERIODIC_TIMEOUT_USEC * r_param * 5) / 4; + + /* resolution of fairness timer */ + fair_periodic_timeout_usec = QM_ARB_BYTES / r_param; + /* for 10G it is 1000usec. for 1G it is 10000usec. */ + t_fair = T_FAIR_COEF / port_rate; + + /* this is the threshold below which we won't arm + the timer anymore */ + m_cmng_port->fair_vars.fair_threshold = QM_ARB_BYTES; + + /* we multiply by 1e3/8 to get bytes/msec. + We don't want the credits to pass a credit + of the T_FAIR*FAIR_MEM (algorithm resolution) */ + m_cmng_port->fair_vars.upper_bound = + r_param * t_fair * FAIR_MEM; + /* since each tick is 4 usec */ + m_cmng_port->fair_vars.fairness_timeout = + fair_periodic_timeout_usec / 4; + + } else { + /* Disable rate shaping and fairness */ + m_cmng_port->flags.cmng_vn_enable = 0; + m_cmng_port->flags.fairness_enable = 0; + m_cmng_port->flags.rate_shaping_enable = 0; + + DP(NETIF_MSG_IFUP, + "Single function mode minmax will be disabled\n"); + } + + /* Store it to internal memory */ + for (i = 0; i < sizeof(struct cmng_struct_per_port) / 4; i++) + REG_WR(bp, BAR_XSTRORM_INTMEM + + XSTORM_CMNG_PER_PORT_VARS_OFFSET(port) + i * 4, + ((u32 *)(m_cmng_port))[i]); +} + +static void bnx2x_init_vn_minmax(struct bnx2x *bp, int func, + u32 wsum, u16 port_rate, + struct cmng_struct_per_port *m_cmng_port) +{ + struct rate_shaping_vars_per_vn m_rs_vn; + struct fairness_vars_per_vn m_fair_vn; + u32 vn_cfg = SHMEM_RD(bp, mf_cfg.func_mf_config[func].config); + u16 vn_min_rate, vn_max_rate; + int i; + + /* If function is hidden - set min and max to zeroes */ + if (vn_cfg & FUNC_MF_CFG_FUNC_HIDE) { + vn_min_rate = 0; + vn_max_rate = 0; + + } else { + vn_min_rate = ((vn_cfg & FUNC_MF_CFG_MIN_BW_MASK) >> + FUNC_MF_CFG_MIN_BW_SHIFT) * 100; + /* If FAIRNESS is enabled (not all min rates are zeroes) and + if current min rate is zero - set it to 1. + This is a requirment of the algorithm. */ + if ((vn_min_rate == 0) && wsum) + vn_min_rate = DEF_MIN_RATE; + vn_max_rate = ((vn_cfg & FUNC_MF_CFG_MAX_BW_MASK) >> + FUNC_MF_CFG_MAX_BW_SHIFT) * 100; + } + + DP(NETIF_MSG_IFUP, "func %d: vn_min_rate=%d vn_max_rate=%d " + "wsum=%d\n", func, vn_min_rate, vn_max_rate, wsum); + + memset(&m_rs_vn, 0, sizeof(struct rate_shaping_vars_per_vn)); + memset(&m_fair_vn, 0, sizeof(struct fairness_vars_per_vn)); + + /* global vn counter - maximal Mbps for this vn */ + m_rs_vn.vn_counter.rate = vn_max_rate; + + /* quota - number of bytes transmitted in this period */ + m_rs_vn.vn_counter.quota = + (vn_max_rate * RS_PERIODIC_TIMEOUT_USEC) / 8; + +#ifdef BNX2X_PER_PROT_QOS + /* per protocol counter */ + for (protocol = 0; protocol < NUM_OF_PROTOCOLS; protocol++) { + /* maximal Mbps for this protocol */ + m_rs_vn.protocol_counters[protocol].rate = + protocol_max_rate[protocol]; + /* the quota in each timer period - + number of bytes transmitted in this period */ + m_rs_vn.protocol_counters[protocol].quota = + (u32)(rs_periodic_timeout_usec * + ((double)m_rs_vn. + protocol_counters[protocol].rate/8)); + } +#endif + + if (wsum) { + /* credit for each period of the fairness algorithm: + number of bytes in T_FAIR (the vn share the port rate). + wsum should not be larger than 10000, thus + T_FAIR_COEF / (8 * wsum) will always be grater than zero */ + m_fair_vn.vn_credit_delta = + max((u64)(vn_min_rate * (T_FAIR_COEF / (8 * wsum))), + (u64)(m_cmng_port->fair_vars.fair_threshold * 2)); + DP(NETIF_MSG_IFUP, "m_fair_vn.vn_credit_delta=%d\n", + m_fair_vn.vn_credit_delta); + } + +#ifdef BNX2X_PER_PROT_QOS + do { + u32 protocolWeightSum = 0; + + for (protocol = 0; protocol < NUM_OF_PROTOCOLS; protocol++) + protocolWeightSum += + drvInit.protocol_min_rate[protocol]; + /* per protocol counter - + NOT NEEDED IF NO PER-PROTOCOL CONGESTION MANAGEMENT */ + if (protocolWeightSum > 0) { + for (protocol = 0; + protocol < NUM_OF_PROTOCOLS; protocol++) + /* credit for each period of the + fairness algorithm - number of bytes in + T_FAIR (the protocol share the vn rate) */ + m_fair_vn.protocol_credit_delta[protocol] = + (u32)((vn_min_rate / 8) * t_fair * + protocol_min_rate / protocolWeightSum); + } + } while (0); +#endif + + /* Store it to internal memory */ + for (i = 0; i < sizeof(struct rate_shaping_vars_per_vn)/4; i++) + REG_WR(bp, BAR_XSTRORM_INTMEM + + XSTORM_RATE_SHAPING_PER_VN_VARS_OFFSET(func) + i * 4, + ((u32 *)(&m_rs_vn))[i]); + + for (i = 0; i < sizeof(struct fairness_vars_per_vn)/4; i++) + REG_WR(bp, BAR_XSTRORM_INTMEM + + XSTORM_FAIRNESS_PER_VN_VARS_OFFSET(func) + i * 4, + ((u32 *)(&m_fair_vn))[i]); +} + /* This function is called upon link interrupt */ static void bnx2x_link_attn(struct bnx2x *bp) { + int vn; + bnx2x_phy_hw_lock(bp); bnx2x_link_update(&bp->link_params, &bp->link_vars); bnx2x_phy_hw_unlock(bp); /* indicate link status */ bnx2x_link_report(bp); + + if (IS_E1HMF(bp)) { + int func; + + for (vn = VN_0; vn < E1HVN_MAX; vn++) { + if (vn == BP_E1HVN(bp)) + continue; + + func = ((vn << 1) | BP_PORT(bp)); + + /* Set the attention towards other drivers + on the same port */ + REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_0 + + (LINK_SYNC_ATTENTION_BIT_FUNC_0 + func)*4, 1); + } + } + + if (CHIP_IS_E1H(bp) && (bp->link_vars.line_speed > 0)) { + struct cmng_struct_per_port m_cmng_port; + u32 wsum; + int port = BP_PORT(bp); + + /* Init RATE SHAPING and FAIRNESS contexts */ + wsum = bnx2x_calc_vn_wsum(bp); + bnx2x_init_port_minmax(bp, (int)wsum, + bp->link_vars.line_speed, + &m_cmng_port); + if (IS_E1HMF(bp)) + for (vn = VN_0; vn < E1HVN_MAX; vn++) + bnx2x_init_vn_minmax(bp, 2*vn + port, + wsum, bp->link_vars.line_speed, + &m_cmng_port); + } } static void bnx2x__link_status_update(struct bnx2x *bp) @@ -1495,6 +1839,20 @@ static void bnx2x__link_status_update(struct bnx2x *bp) bnx2x_link_report(bp); } +static void bnx2x_pmf_update(struct bnx2x *bp) +{ + int port = BP_PORT(bp); + u32 val; + + bp->port.pmf = 1; + DP(NETIF_MSG_LINK, "pmf %d\n", bp->port.pmf); + + /* enable nig attention */ + val = (0xff0f | (1 << (BP_E1HVN(bp) + 4))); + REG_WR(bp, HC_REG_TRAILING_EDGE_0 + port*8, val); + REG_WR(bp, HC_REG_LEADING_EDGE_0 + port*8, val); +} + /* end of Link */ /* slow path */ @@ -1507,10 +1865,10 @@ static void bnx2x__link_status_update(struct bnx2x *bp) static int bnx2x_sp_post(struct bnx2x *bp, int command, int cid, u32 data_hi, u32 data_lo, int common) { - int port = bp->port; + int func = BP_FUNC(bp); - DP(NETIF_MSG_TIMER, - "spe (%x:%x) command %d hw_cid %x data (%x:%x) left %x\n", + DP(BNX2X_MSG_SP/*NETIF_MSG_TIMER*/, + "SPQE (%x:%x) command %d hw_cid %x data (%x:%x) left %x\n", (u32)U64_HI(bp->spq_mapping), (u32)(U64_LO(bp->spq_mapping) + (void *)bp->spq_prod_bd - (void *)bp->spq), command, HW_CID(bp, cid), data_hi, data_lo, bp->spq_left); @@ -1520,11 +1878,11 @@ static int bnx2x_sp_post(struct bnx2x *bp, int command, int cid, return -EIO; #endif - spin_lock(&bp->spq_lock); + spin_lock_bh(&bp->spq_lock); if (!bp->spq_left) { BNX2X_ERR("BUG! SPQ ring full!\n"); - spin_unlock(&bp->spq_lock); + spin_unlock_bh(&bp->spq_lock); bnx2x_panic(); return -EBUSY; } @@ -1553,18 +1911,18 @@ static int bnx2x_sp_post(struct bnx2x *bp, int command, int cid, bp->spq_prod_idx++; } - REG_WR(bp, BAR_XSTRORM_INTMEM + XSTORM_SPQ_PROD_OFFSET(port), + REG_WR(bp, BAR_XSTRORM_INTMEM + XSTORM_SPQ_PROD_OFFSET(func), bp->spq_prod_idx); - spin_unlock(&bp->spq_lock); + spin_unlock_bh(&bp->spq_lock); return 0; } /* acquire split MCP access lock register */ static int bnx2x_lock_alr(struct bnx2x *bp) { - int rc = 0; u32 i, j, val; + int rc = 0; might_sleep(); i = 100; @@ -1577,10 +1935,8 @@ static int bnx2x_lock_alr(struct bnx2x *bp) msleep(5); } - if (!(val & (1L << 31))) { BNX2X_ERR("Cannot acquire nvram interface\n"); - rc = -EBUSY; } @@ -1631,8 +1987,9 @@ static inline u16 bnx2x_update_dsb_idx(struct bnx2x *bp) static void bnx2x_attn_int_asserted(struct bnx2x *bp, u32 asserted) { - int port = bp->port; - u32 igu_addr = (IGU_ADDR_ATTN_BITS_SET + IGU_PORT_BASE * port) * 8; + int port = BP_PORT(bp); + int func = BP_FUNC(bp); + u32 igu_addr = (IGU_ADDR_ATTN_BITS_SET + IGU_FUNC_BASE * func) * 8; u32 aeu_addr = port ? MISC_REG_AEU_MASK_ATTN_FUNC_1 : MISC_REG_AEU_MASK_ATTN_FUNC_0; u32 nig_int_mask_addr = port ? NIG_REG_MASK_INTERRUPT_PORT1 : @@ -1716,14 +2073,14 @@ static void bnx2x_attn_int_asserted(struct bnx2x *bp, u32 asserted) static inline void bnx2x_attn_int_deasserted0(struct bnx2x *bp, u32 attn) { - int port = bp->port; + int port = BP_PORT(bp); int reg_offset; u32 val; - if (attn & AEU_INPUTS_ATTN_BITS_SPIO5) { + reg_offset = (port ? MISC_REG_AEU_ENABLE1_FUNC_1_OUT_0 : + MISC_REG_AEU_ENABLE1_FUNC_0_OUT_0); - reg_offset = (port ? MISC_REG_AEU_ENABLE1_FUNC_1_OUT_0 : - MISC_REG_AEU_ENABLE1_FUNC_0_OUT_0); + if (attn & AEU_INPUTS_ATTN_BITS_SPIO5) { val = REG_RD(bp, reg_offset); val &= ~AEU_INPUTS_ATTN_BITS_SPIO5; @@ -1731,7 +2088,7 @@ static inline void bnx2x_attn_int_deasserted0(struct bnx2x *bp, u32 attn) BNX2X_ERR("SPIO5 hw attention\n"); - switch (bp->board & SHARED_HW_CFG_BOARD_TYPE_MASK) { + switch (bp->common.board & SHARED_HW_CFG_BOARD_TYPE_MASK) { case SHARED_HW_CFG_BOARD_TYPE_BCM957710A1022G: /* Fan failure attention */ @@ -1762,6 +2119,17 @@ static inline void bnx2x_attn_int_deasserted0(struct bnx2x *bp, u32 attn) break; } } + + if (attn & HW_INTERRUT_ASSERT_SET_0) { + + val = REG_RD(bp, reg_offset); + val &= ~(attn & HW_INTERRUT_ASSERT_SET_0); + REG_WR(bp, reg_offset, val); + + BNX2X_ERR("FATAL HW block attention set0 0x%x\n", + (attn & HW_INTERRUT_ASSERT_SET_0)); + bnx2x_panic(); + } } static inline void bnx2x_attn_int_deasserted1(struct bnx2x *bp, u32 attn) @@ -1776,6 +2144,23 @@ static inline void bnx2x_attn_int_deasserted1(struct bnx2x *bp, u32 attn) if (val & 0x2) BNX2X_ERR("FATAL error from DORQ\n"); } + + if (attn & HW_INTERRUT_ASSERT_SET_1) { + + int port = BP_PORT(bp); + int reg_offset; + + reg_offset = (port ? MISC_REG_AEU_ENABLE1_FUNC_1_OUT_1 : + MISC_REG_AEU_ENABLE1_FUNC_0_OUT_1); + + val = REG_RD(bp, reg_offset); + val &= ~(attn & HW_INTERRUT_ASSERT_SET_1); + REG_WR(bp, reg_offset, val); + + BNX2X_ERR("FATAL HW block attention set1 0x%x\n", + (attn & HW_INTERRUT_ASSERT_SET_1)); + bnx2x_panic(); + } } static inline void bnx2x_attn_int_deasserted2(struct bnx2x *bp, u32 attn) @@ -1799,13 +2184,41 @@ static inline void bnx2x_attn_int_deasserted2(struct bnx2x *bp, u32 attn) if (val & 0x18000) BNX2X_ERR("FATAL error from PXP\n"); } + + if (attn & HW_INTERRUT_ASSERT_SET_2) { + + int port = BP_PORT(bp); + int reg_offset; + + reg_offset = (port ? MISC_REG_AEU_ENABLE1_FUNC_1_OUT_2 : + MISC_REG_AEU_ENABLE1_FUNC_0_OUT_2); + + val = REG_RD(bp, reg_offset); + val &= ~(attn & HW_INTERRUT_ASSERT_SET_2); + REG_WR(bp, reg_offset, val); + + BNX2X_ERR("FATAL HW block attention set2 0x%x\n", + (attn & HW_INTERRUT_ASSERT_SET_2)); + bnx2x_panic(); + } } static inline void bnx2x_attn_int_deasserted3(struct bnx2x *bp, u32 attn) { + u32 val; + if (attn & EVEREST_GEN_ATTN_IN_USE_MASK) { - if (attn & BNX2X_MC_ASSERT_BITS) { + if (attn & BNX2X_PMF_LINK_ASSERT) { + int func = BP_FUNC(bp); + + REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_12 + func*4, 0); + bnx2x__link_status_update(bp); + if (SHMEM_RD(bp, func_mb[func].drv_status) & + DRV_STATUS_PMF) + bnx2x_pmf_update(bp); + + } else if (attn & BNX2X_MC_ASSERT_BITS) { BNX2X_ERR("MC assert!\n"); REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_10, 0); @@ -1818,16 +2231,25 @@ static inline void bnx2x_attn_int_deasserted3(struct bnx2x *bp, u32 attn) BNX2X_ERR("MCP assert!\n"); REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_11, 0); - bnx2x_mc_assert(bp); + bnx2x_fw_dump(bp); } else BNX2X_ERR("Unknown HW assert! (attn 0x%x)\n", attn); } if (attn & EVEREST_LATCHED_ATTN_IN_USE_MASK) { - + BNX2X_ERR("LATCHED attention 0x%08x (masked)\n", attn); + if (attn & BNX2X_GRC_TIMEOUT) { + val = CHIP_IS_E1H(bp) ? + REG_RD(bp, MISC_REG_GRC_TIMEOUT_ATTN) : 0; + BNX2X_ERR("GRC time-out 0x%08x\n", val); + } + if (attn & BNX2X_GRC_RSV) { + val = CHIP_IS_E1H(bp) ? + REG_RD(bp, MISC_REG_GRC_RSV_ATTN) : 0; + BNX2X_ERR("GRC reserved 0x%08x\n", val); + } REG_WR(bp, MISC_REG_AEU_CLR_LATCH_SIGNAL, 0x7ff); - BNX2X_ERR("LATCHED attention 0x%x (masked)\n", attn); } } @@ -1835,7 +2257,7 @@ static void bnx2x_attn_int_deasserted(struct bnx2x *bp, u32 deasserted) { struct attn_route attn; struct attn_route group_mask; - int port = bp->port; + int port = BP_PORT(bp); int index; u32 reg_addr; u32 val; @@ -1848,14 +2270,16 @@ static void bnx2x_attn_int_deasserted(struct bnx2x *bp, u32 deasserted) attn.sig[1] = REG_RD(bp, MISC_REG_AEU_AFTER_INVERT_2_FUNC_0 + port*4); attn.sig[2] = REG_RD(bp, MISC_REG_AEU_AFTER_INVERT_3_FUNC_0 + port*4); attn.sig[3] = REG_RD(bp, MISC_REG_AEU_AFTER_INVERT_4_FUNC_0 + port*4); - DP(NETIF_MSG_HW, "attn %llx\n", (unsigned long long)attn.sig[0]); + DP(NETIF_MSG_HW, "attn: %08x %08x %08x %08x\n", + attn.sig[0], attn.sig[1], attn.sig[2], attn.sig[3]); for (index = 0; index < MAX_DYNAMIC_ATTN_GRPS; index++) { if (deasserted & (1 << index)) { group_mask = bp->attn_group[index]; - DP(NETIF_MSG_HW, "group[%d]: %llx\n", index, - (unsigned long long)group_mask.sig[0]); + DP(NETIF_MSG_HW, "group[%d]: %08x %08x %08x %08x\n", + index, group_mask.sig[0], group_mask.sig[1], + group_mask.sig[2], group_mask.sig[3]); bnx2x_attn_int_deasserted3(bp, attn.sig[3] & group_mask.sig[3]); @@ -1866,22 +2290,6 @@ static void bnx2x_attn_int_deasserted(struct bnx2x *bp, u32 deasserted) bnx2x_attn_int_deasserted0(bp, attn.sig[0] & group_mask.sig[0]); - if ((attn.sig[0] & group_mask.sig[0] & - HW_INTERRUT_ASSERT_SET_0) || - (attn.sig[1] & group_mask.sig[1] & - HW_INTERRUT_ASSERT_SET_1) || - (attn.sig[2] & group_mask.sig[2] & - HW_INTERRUT_ASSERT_SET_2)) - BNX2X_ERR("FATAL HW block attention" - " set0 0x%x set1 0x%x" - " set2 0x%x\n", - (attn.sig[0] & group_mask.sig[0] & - HW_INTERRUT_ASSERT_SET_0), - (attn.sig[1] & group_mask.sig[1] & - HW_INTERRUT_ASSERT_SET_1), - (attn.sig[2] & group_mask.sig[2] & - HW_INTERRUT_ASSERT_SET_2)); - if ((attn.sig[0] & group_mask.sig[0] & HW_PRTY_ASSERT_SET_0) || (attn.sig[1] & group_mask.sig[1] & @@ -1894,17 +2302,17 @@ static void bnx2x_attn_int_deasserted(struct bnx2x *bp, u32 deasserted) bnx2x_unlock_alr(bp); - reg_addr = (IGU_ADDR_ATTN_BITS_CLR + IGU_PORT_BASE * port) * 8; + reg_addr = (IGU_ADDR_ATTN_BITS_CLR + IGU_FUNC_BASE * BP_FUNC(bp)) * 8; val = ~deasserted; -/* DP(NETIF_MSG_INTR, "write 0x%08x to IGU addr 0x%x\n", +/* DP(NETIF_MSG_INTR, "write 0x%08x to IGU addr 0x%x\n", val, BAR_IGU_INTMEM + reg_addr); */ REG_WR(bp, BAR_IGU_INTMEM + reg_addr, val); if (bp->aeu_mask & (deasserted & 0xff)) - BNX2X_ERR("IGU BUG\n"); + BNX2X_ERR("IGU BUG!\n"); if (~bp->attn_state & deasserted) - BNX2X_ERR("IGU BUG\n"); + BNX2X_ERR("IGU BUG!\n"); reg_addr = port ? MISC_REG_AEU_MASK_ATTN_FUNC_1 : MISC_REG_AEU_MASK_ATTN_FUNC_0; @@ -1936,7 +2344,7 @@ static void bnx2x_attn_int(struct bnx2x *bp) attn_bits, attn_ack, asserted, deasserted); if (~(attn_bits ^ attn_ack) & (attn_bits ^ attn_state)) - BNX2X_ERR("bad attention state\n"); + BNX2X_ERR("BAD attention state\n"); /* handle bits that were raised */ if (asserted) @@ -1951,6 +2359,7 @@ static void bnx2x_sp_task(struct work_struct *work) struct bnx2x *bp = container_of(work, struct bnx2x, sp_task); u16 status; + /* Return here if interrupt is disabled */ if (unlikely(atomic_read(&bp->intr_sem) != 0)) { DP(BNX2X_MSG_SP, "called but intr_sem not 0, returning\n"); @@ -1958,19 +2367,15 @@ static void bnx2x_sp_task(struct work_struct *work) } status = bnx2x_update_dsb_idx(bp); - if (status == 0) - BNX2X_ERR("spurious slowpath interrupt!\n"); +/* if (status == 0) */ +/* BNX2X_ERR("spurious slowpath interrupt!\n"); */ - DP(NETIF_MSG_INTR, "got a slowpath interrupt (updated %x)\n", status); + DP(BNX2X_MSG_SP, "got a slowpath interrupt (updated %x)\n", status); /* HW attentions */ if (status & 0x1) bnx2x_attn_int(bp); - /* CStorm events: query_stats, port delete ramrod */ - if (status & 0x2) - bp->stat_pending = 0; - bnx2x_ack_sb(bp, DEF_SB_ID, ATTENTION_ID, bp->def_att_idx, IGU_INT_NOP, 1); bnx2x_ack_sb(bp, DEF_SB_ID, USTORM_ID, le16_to_cpu(bp->def_u_idx), @@ -2109,13 +2514,13 @@ static inline long bnx2x_hilo(u32 *hiref) static void bnx2x_init_mac_stats(struct bnx2x *bp) { struct dmae_command *dmae; - int port = bp->port; + int port = BP_PORT(bp); int loader_idx = port * 8; u32 opcode; u32 mac_addr; bp->executer_idx = 0; - if (bp->fw_mb) { + if (bp->func_stx) { /* MCP */ opcode = (DMAE_CMD_SRC_PCI | DMAE_CMD_DST_GRC | DMAE_CMD_SRC_RESET | DMAE_CMD_DST_RESET | @@ -2135,7 +2540,7 @@ static void bnx2x_init_mac_stats(struct bnx2x *bp) sizeof(u32)); dmae->src_addr_hi = U64_HI(bnx2x_sp_mapping(bp, eth_stats) + sizeof(u32)); - dmae->dst_addr_lo = bp->fw_mb >> 2; + dmae->dst_addr_lo = bp->func_stx >> 2; dmae->dst_addr_hi = 0; dmae->len = (offsetof(struct bnx2x_eth_stats, mac_stx_end) - sizeof(u32)) >> 2; @@ -2280,7 +2685,7 @@ static void bnx2x_init_mac_stats(struct bnx2x *bp) static void bnx2x_init_stats(struct bnx2x *bp) { - int port = bp->port; + int port = BP_PORT(bp); bp->stats_state = STATS_STATE_DISABLE; bp->executer_idx = 0; @@ -2641,8 +3046,6 @@ static void bnx2x_update_net_stats(struct bnx2x *bp) static void bnx2x_update_stats(struct bnx2x *bp) { - int i; - if (!bnx2x_update_storm_stats(bp)) { if (bp->link_vars.mac_type == MAC_TYPE_BMAC) { @@ -2662,6 +3065,7 @@ static void bnx2x_update_stats(struct bnx2x *bp) if (bp->msglevel & NETIF_MSG_TIMER) { struct bnx2x_eth_stats *estats = bnx2x_sp(bp, eth_stats); struct net_device_stats *nstats = &bp->dev->stats; + int i; printk(KERN_DEBUG "%s:\n", bp->dev->name); printk(KERN_DEBUG " tx avail (%4x) tx hc idx (%x)" @@ -2707,7 +3111,7 @@ static void bnx2x_update_stats(struct bnx2x *bp) /* loader */ if (bp->executer_idx) { struct dmae_command *dmae = &bp->dmae; - int port = bp->port; + int port = BP_PORT(bp); int loader_idx = port * 8; memset(dmae, 0, sizeof(struct dmae_command)); @@ -2766,8 +3170,8 @@ static void bnx2x_timer(unsigned long data) rc = bnx2x_rx_int(fp, 1000); } - if (!nomcp) { - int port = bp->port; + if (!BP_NOMCP(bp)) { + int func = BP_FUNC(bp); u32 drv_pulse; u32 mcp_pulse; @@ -2775,9 +3179,9 @@ static void bnx2x_timer(unsigned long data) bp->fw_drv_pulse_wr_seq &= DRV_PULSE_SEQ_MASK; /* TBD - add SYSTEM_TIME */ drv_pulse = bp->fw_drv_pulse_wr_seq; - SHMEM_WR(bp, func_mb[port].drv_pulse_mb, drv_pulse); + SHMEM_WR(bp, func_mb[func].drv_pulse_mb, drv_pulse); - mcp_pulse = (SHMEM_RD(bp, func_mb[port].mcp_pulse_mb) & + mcp_pulse = (SHMEM_RD(bp, func_mb[func].mcp_pulse_mb) & MCP_PULSE_SEQ_MASK); /* The delta between driver pulse and mcp response * should be 1 (before mcp response) or 0 (after mcp response) @@ -2807,58 +3211,89 @@ timer_restart: * nic init service functions */ -static void bnx2x_init_sb(struct bnx2x *bp, struct host_status_block *sb, - dma_addr_t mapping, int id) +static void bnx2x_zero_sb(struct bnx2x *bp, int sb_id) { - int port = bp->port; - u64 section; + int port = BP_PORT(bp); + + bnx2x_init_fill(bp, BAR_USTRORM_INTMEM + + USTORM_SB_HOST_STATUS_BLOCK_OFFSET(port, sb_id), 0, + sizeof(struct ustorm_def_status_block)/4); + bnx2x_init_fill(bp, BAR_CSTRORM_INTMEM + + CSTORM_SB_HOST_STATUS_BLOCK_OFFSET(port, sb_id), 0, + sizeof(struct cstorm_def_status_block)/4); +} + +static void bnx2x_init_sb(struct bnx2x *bp, int sb_id, + struct host_status_block *sb, dma_addr_t mapping) +{ + int port = BP_PORT(bp); int index; + u64 section; /* USTORM */ section = ((u64)mapping) + offsetof(struct host_status_block, u_status_block); - sb->u_status_block.status_block_id = id; + sb->u_status_block.status_block_id = sb_id; REG_WR(bp, BAR_USTRORM_INTMEM + - USTORM_SB_HOST_SB_ADDR_OFFSET(port, id), U64_LO(section)); + USTORM_SB_HOST_SB_ADDR_OFFSET(port, sb_id), U64_LO(section)); REG_WR(bp, BAR_USTRORM_INTMEM + - ((USTORM_SB_HOST_SB_ADDR_OFFSET(port, id)) + 4), + ((USTORM_SB_HOST_SB_ADDR_OFFSET(port, sb_id)) + 4), U64_HI(section)); for (index = 0; index < HC_USTORM_SB_NUM_INDICES; index++) REG_WR16(bp, BAR_USTRORM_INTMEM + - USTORM_SB_HC_DISABLE_OFFSET(port, id, index), 0x1); + USTORM_SB_HC_DISABLE_OFFSET(port, sb_id, index), 1); /* CSTORM */ section = ((u64)mapping) + offsetof(struct host_status_block, c_status_block); - sb->c_status_block.status_block_id = id; + sb->c_status_block.status_block_id = sb_id; REG_WR(bp, BAR_CSTRORM_INTMEM + - CSTORM_SB_HOST_SB_ADDR_OFFSET(port, id), U64_LO(section)); + CSTORM_SB_HOST_SB_ADDR_OFFSET(port, sb_id), U64_LO(section)); REG_WR(bp, BAR_CSTRORM_INTMEM + - ((CSTORM_SB_HOST_SB_ADDR_OFFSET(port, id)) + 4), + ((CSTORM_SB_HOST_SB_ADDR_OFFSET(port, sb_id)) + 4), U64_HI(section)); for (index = 0; index < HC_CSTORM_SB_NUM_INDICES; index++) REG_WR16(bp, BAR_CSTRORM_INTMEM + - CSTORM_SB_HC_DISABLE_OFFSET(port, id, index), 0x1); + CSTORM_SB_HC_DISABLE_OFFSET(port, sb_id, index), 1); + + bnx2x_ack_sb(bp, sb_id, CSTORM_ID, 0, IGU_INT_ENABLE, 0); +} + +static void bnx2x_zero_def_sb(struct bnx2x *bp) +{ + int func = BP_FUNC(bp); - bnx2x_ack_sb(bp, id, CSTORM_ID, 0, IGU_INT_ENABLE, 0); + bnx2x_init_fill(bp, BAR_USTRORM_INTMEM + + USTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(func), 0, + sizeof(struct ustorm_def_status_block)/4); + bnx2x_init_fill(bp, BAR_CSTRORM_INTMEM + + CSTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(func), 0, + sizeof(struct cstorm_def_status_block)/4); + bnx2x_init_fill(bp, BAR_XSTRORM_INTMEM + + XSTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(func), 0, + sizeof(struct xstorm_def_status_block)/4); + bnx2x_init_fill(bp, BAR_TSTRORM_INTMEM + + TSTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(func), 0, + sizeof(struct tstorm_def_status_block)/4); } static void bnx2x_init_def_sb(struct bnx2x *bp, struct host_def_status_block *def_sb, - dma_addr_t mapping, int id) + dma_addr_t mapping, int sb_id) { - int port = bp->port; + int port = BP_PORT(bp); + int func = BP_FUNC(bp); int index, val, reg_offset; u64 section; /* ATTN */ section = ((u64)mapping) + offsetof(struct host_def_status_block, atten_status_block); - def_sb->atten_status_block.status_block_id = id; + def_sb->atten_status_block.status_block_id = sb_id; bp->def_att_idx = 0; bp->attn_state = 0; @@ -2866,7 +3301,7 @@ static void bnx2x_init_def_sb(struct bnx2x *bp, reg_offset = (port ? MISC_REG_AEU_ENABLE1_FUNC_1_OUT_0 : MISC_REG_AEU_ENABLE1_FUNC_0_OUT_0); - for (index = 0; index < 3; index++) { + for (index = 0; index < MAX_DYNAMIC_ATTN_GRPS; index++) { bp->attn_group[index].sig[0] = REG_RD(bp, reg_offset + 0x10*index); bp->attn_group[index].sig[1] = REG_RD(bp, @@ -2889,116 +3324,123 @@ static void bnx2x_init_def_sb(struct bnx2x *bp, reg_offset = (port ? HC_REG_ATTN_NUM_P1 : HC_REG_ATTN_NUM_P0); val = REG_RD(bp, reg_offset); - val |= id; + val |= sb_id; REG_WR(bp, reg_offset, val); /* USTORM */ section = ((u64)mapping) + offsetof(struct host_def_status_block, u_def_status_block); - def_sb->u_def_status_block.status_block_id = id; + def_sb->u_def_status_block.status_block_id = sb_id; bp->def_u_idx = 0; REG_WR(bp, BAR_USTRORM_INTMEM + - USTORM_DEF_SB_HOST_SB_ADDR_OFFSET(port), U64_LO(section)); + USTORM_DEF_SB_HOST_SB_ADDR_OFFSET(func), U64_LO(section)); REG_WR(bp, BAR_USTRORM_INTMEM + - ((USTORM_DEF_SB_HOST_SB_ADDR_OFFSET(port)) + 4), + ((USTORM_DEF_SB_HOST_SB_ADDR_OFFSET(func)) + 4), U64_HI(section)); - REG_WR(bp, BAR_USTRORM_INTMEM + USTORM_HC_BTR_OFFSET(port), + REG_WR8(bp, BAR_USTRORM_INTMEM + DEF_USB_FUNC_OFF + + USTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(func), func); + REG_WR(bp, BAR_USTRORM_INTMEM + USTORM_HC_BTR_OFFSET(func), BNX2X_BTR); for (index = 0; index < HC_USTORM_DEF_SB_NUM_INDICES; index++) REG_WR16(bp, BAR_USTRORM_INTMEM + - USTORM_DEF_SB_HC_DISABLE_OFFSET(port, index), 0x1); + USTORM_DEF_SB_HC_DISABLE_OFFSET(func, index), 1); /* CSTORM */ section = ((u64)mapping) + offsetof(struct host_def_status_block, c_def_status_block); - def_sb->c_def_status_block.status_block_id = id; + def_sb->c_def_status_block.status_block_id = sb_id; bp->def_c_idx = 0; REG_WR(bp, BAR_CSTRORM_INTMEM + - CSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(port), U64_LO(section)); + CSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(func), U64_LO(section)); REG_WR(bp, BAR_CSTRORM_INTMEM + - ((CSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(port)) + 4), + ((CSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(func)) + 4), U64_HI(section)); - REG_WR(bp, BAR_CSTRORM_INTMEM + CSTORM_HC_BTR_OFFSET(port), + REG_WR8(bp, BAR_CSTRORM_INTMEM + DEF_CSB_FUNC_OFF + + CSTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(func), func); + REG_WR(bp, BAR_CSTRORM_INTMEM + CSTORM_HC_BTR_OFFSET(func), BNX2X_BTR); for (index = 0; index < HC_CSTORM_DEF_SB_NUM_INDICES; index++) REG_WR16(bp, BAR_CSTRORM_INTMEM + - CSTORM_DEF_SB_HC_DISABLE_OFFSET(port, index), 0x1); + CSTORM_DEF_SB_HC_DISABLE_OFFSET(func, index), 1); /* TSTORM */ section = ((u64)mapping) + offsetof(struct host_def_status_block, t_def_status_block); - def_sb->t_def_status_block.status_block_id = id; + def_sb->t_def_status_block.status_block_id = sb_id; bp->def_t_idx = 0; REG_WR(bp, BAR_TSTRORM_INTMEM + - TSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(port), U64_LO(section)); + TSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(func), U64_LO(section)); REG_WR(bp, BAR_TSTRORM_INTMEM + - ((TSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(port)) + 4), + ((TSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(func)) + 4), U64_HI(section)); - REG_WR(bp, BAR_TSTRORM_INTMEM + TSTORM_HC_BTR_OFFSET(port), + REG_WR8(bp, BAR_TSTRORM_INTMEM + DEF_TSB_FUNC_OFF + + TSTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(func), func); + REG_WR(bp, BAR_TSTRORM_INTMEM + TSTORM_HC_BTR_OFFSET(func), BNX2X_BTR); for (index = 0; index < HC_TSTORM_DEF_SB_NUM_INDICES; index++) REG_WR16(bp, BAR_TSTRORM_INTMEM + - TSTORM_DEF_SB_HC_DISABLE_OFFSET(port, index), 0x1); + TSTORM_DEF_SB_HC_DISABLE_OFFSET(func, index), 1); /* XSTORM */ section = ((u64)mapping) + offsetof(struct host_def_status_block, x_def_status_block); - def_sb->x_def_status_block.status_block_id = id; + def_sb->x_def_status_block.status_block_id = sb_id; bp->def_x_idx = 0; REG_WR(bp, BAR_XSTRORM_INTMEM + - XSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(port), U64_LO(section)); + XSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(func), U64_LO(section)); REG_WR(bp, BAR_XSTRORM_INTMEM + - ((XSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(port)) + 4), + ((XSTORM_DEF_SB_HOST_SB_ADDR_OFFSET(func)) + 4), U64_HI(section)); - REG_WR(bp, BAR_XSTRORM_INTMEM + XSTORM_HC_BTR_OFFSET(port), + REG_WR8(bp, BAR_XSTRORM_INTMEM + DEF_XSB_FUNC_OFF + + XSTORM_DEF_SB_HOST_STATUS_BLOCK_OFFSET(func), func); + REG_WR(bp, BAR_XSTRORM_INTMEM + XSTORM_HC_BTR_OFFSET(func), BNX2X_BTR); for (index = 0; index < HC_XSTORM_DEF_SB_NUM_INDICES; index++) REG_WR16(bp, BAR_XSTRORM_INTMEM + - XSTORM_DEF_SB_HC_DISABLE_OFFSET(port, index), 0x1); - - bp->stat_pending = 0; + XSTORM_DEF_SB_HC_DISABLE_OFFSET(func, index), 1); - bnx2x_ack_sb(bp, id, CSTORM_ID, 0, IGU_INT_ENABLE, 0); + bnx2x_ack_sb(bp, sb_id, CSTORM_ID, 0, IGU_INT_ENABLE, 0); } static void bnx2x_update_coalesce(struct bnx2x *bp) { - int port = bp->port; + int port = BP_PORT(bp); int i; for_each_queue(bp, i) { + int sb_id = bp->fp[i].sb_id; /* HC_INDEX_U_ETH_RX_CQ_CONS */ REG_WR8(bp, BAR_USTRORM_INTMEM + - USTORM_SB_HC_TIMEOUT_OFFSET(port, i, + USTORM_SB_HC_TIMEOUT_OFFSET(port, sb_id, HC_INDEX_U_ETH_RX_CQ_CONS), - bp->rx_ticks_int/12); + bp->rx_ticks/12); REG_WR16(bp, BAR_USTRORM_INTMEM + - USTORM_SB_HC_DISABLE_OFFSET(port, i, + USTORM_SB_HC_DISABLE_OFFSET(port, sb_id, HC_INDEX_U_ETH_RX_CQ_CONS), - bp->rx_ticks_int ? 0 : 1); + bp->rx_ticks ? 0 : 1); /* HC_INDEX_C_ETH_TX_CQ_CONS */ REG_WR8(bp, BAR_CSTRORM_INTMEM + - CSTORM_SB_HC_TIMEOUT_OFFSET(port, i, + CSTORM_SB_HC_TIMEOUT_OFFSET(port, sb_id, HC_INDEX_C_ETH_TX_CQ_CONS), - bp->tx_ticks_int/12); + bp->tx_ticks/12); REG_WR16(bp, BAR_CSTRORM_INTMEM + - CSTORM_SB_HC_DISABLE_OFFSET(port, i, + CSTORM_SB_HC_DISABLE_OFFSET(port, sb_id, HC_INDEX_C_ETH_TX_CQ_CONS), - bp->tx_ticks_int ? 0 : 1); + bp->tx_ticks ? 0 : 1); } } @@ -3006,7 +3448,6 @@ static void bnx2x_init_rx_rings(struct bnx2x *bp) { u16 ring_prod; int i, j; - int port = bp->port; bp->rx_buf_use_size = bp->dev->mtu; @@ -3025,13 +3466,13 @@ static void bnx2x_init_rx_rings(struct bnx2x *bp) rx_bd = &fp->rx_desc_ring[RX_DESC_CNT * i - 2]; rx_bd->addr_hi = cpu_to_le32(U64_HI(fp->rx_desc_mapping + - BCM_PAGE_SIZE*(i % NUM_RX_RINGS))); + BCM_PAGE_SIZE*(i % NUM_RX_RINGS))); rx_bd->addr_lo = cpu_to_le32(U64_LO(fp->rx_desc_mapping + - BCM_PAGE_SIZE*(i % NUM_RX_RINGS))); - + BCM_PAGE_SIZE*(i % NUM_RX_RINGS))); } + /* CQ ring */ for (i = 1; i <= NUM_RCQ_RINGS; i++) { struct eth_rx_cqe_next_page *nextpg; @@ -3039,10 +3480,10 @@ static void bnx2x_init_rx_rings(struct bnx2x *bp) &fp->rx_comp_ring[RCQ_DESC_CNT * i - 1]; nextpg->addr_hi = cpu_to_le32(U64_HI(fp->rx_comp_mapping + - BCM_PAGE_SIZE*(i % NUM_RCQ_RINGS))); + BCM_PAGE_SIZE*(i % NUM_RCQ_RINGS))); nextpg->addr_lo = cpu_to_le32(U64_LO(fp->rx_comp_mapping + - BCM_PAGE_SIZE*(i % NUM_RCQ_RINGS))); + BCM_PAGE_SIZE*(i % NUM_RCQ_RINGS))); } /* rx completion queue */ @@ -3064,15 +3505,16 @@ static void bnx2x_init_rx_rings(struct bnx2x *bp) /* Warning! this will generate an interrupt (to the TSTORM) */ /* must only be done when chip is initialized */ REG_WR(bp, BAR_TSTRORM_INTMEM + - TSTORM_RCQ_PROD_OFFSET(port, j), ring_prod); + TSTORM_RX_PRODS_OFFSET(BP_PORT(bp), FP_CL_ID(fp)), + ring_prod); if (j != 0) continue; REG_WR(bp, BAR_USTRORM_INTMEM + - USTORM_MEM_WORKAROUND_ADDRESS_OFFSET(port), + USTORM_MEM_WORKAROUND_ADDRESS_OFFSET(BP_PORT(bp)), U64_LO(fp->rx_comp_mapping)); REG_WR(bp, BAR_USTRORM_INTMEM + - USTORM_MEM_WORKAROUND_ADDRESS_OFFSET(port) + 4, + USTORM_MEM_WORKAROUND_ADDRESS_OFFSET(BP_PORT(bp)) + 4, U64_HI(fp->rx_comp_mapping)); } } @@ -3090,10 +3532,10 @@ static void bnx2x_init_tx_ring(struct bnx2x *bp) tx_bd->addr_hi = cpu_to_le32(U64_HI(fp->tx_desc_mapping + - BCM_PAGE_SIZE*(i % NUM_TX_RINGS))); + BCM_PAGE_SIZE*(i % NUM_TX_RINGS))); tx_bd->addr_lo = cpu_to_le32(U64_LO(fp->tx_desc_mapping + - BCM_PAGE_SIZE*(i % NUM_TX_RINGS))); + BCM_PAGE_SIZE*(i % NUM_TX_RINGS))); } fp->tx_pkt_prod = 0; @@ -3107,7 +3549,7 @@ static void bnx2x_init_tx_ring(struct bnx2x *bp) static void bnx2x_init_sp_ring(struct bnx2x *bp) { - int port = bp->port; + int func = BP_FUNC(bp); spin_lock_init(&bp->spq_lock); @@ -3117,12 +3559,13 @@ static void bnx2x_init_sp_ring(struct bnx2x *bp) bp->spq_prod_bd = bp->spq; bp->spq_last_bd = bp->spq_prod_bd + MAX_SP_DESC_CNT; - REG_WR(bp, BAR_XSTRORM_INTMEM + XSTORM_SPQ_PAGE_BASE_OFFSET(port), + REG_WR(bp, XSEM_REG_FAST_MEMORY + XSTORM_SPQ_PAGE_BASE_OFFSET(func), U64_LO(bp->spq_mapping)); - REG_WR(bp, BAR_XSTRORM_INTMEM + XSTORM_SPQ_PAGE_BASE_OFFSET(port) + 4, + REG_WR(bp, + XSEM_REG_FAST_MEMORY + XSTORM_SPQ_PAGE_BASE_OFFSET(func) + 4, U64_HI(bp->spq_mapping)); - REG_WR(bp, XSEM_REG_FAST_MEMORY + XSTORM_SPQ_PROD_OFFSET(port), + REG_WR(bp, XSEM_REG_FAST_MEMORY + XSTORM_SPQ_PROD_OFFSET(func), bp->spq_prod_idx); } @@ -3133,6 +3576,7 @@ static void bnx2x_init_context(struct bnx2x *bp) for_each_queue(bp, i) { struct eth_context *context = bnx2x_sp(bp, context[i].eth); struct bnx2x_fastpath *fp = &bp->fp[i]; + u8 sb_id = FP_SB_ID(fp); context->xstorm_st_context.tx_bd_page_base_hi = U64_HI(fp->tx_desc_mapping); @@ -3142,26 +3586,25 @@ static void bnx2x_init_context(struct bnx2x *bp) U64_HI(fp->tx_prods_mapping); context->xstorm_st_context.db_data_addr_lo = U64_LO(fp->tx_prods_mapping); - - context->ustorm_st_context.rx_bd_page_base_hi = + context->xstorm_st_context.statistics_data = (BP_CL_ID(bp) | + XSTORM_ETH_ST_CONTEXT_STATISTICS_ENABLE); + + context->ustorm_st_context.common.sb_index_numbers = + BNX2X_RX_SB_INDEX_NUM; + context->ustorm_st_context.common.clientId = FP_CL_ID(fp); + context->ustorm_st_context.common.status_block_id = sb_id; + context->ustorm_st_context.common.flags = + USTORM_ETH_ST_CONTEXT_CONFIG_ENABLE_MC_ALIGNMENT; + context->ustorm_st_context.common.mc_alignment_size = 64; + context->ustorm_st_context.common.bd_buff_size = + bp->rx_buf_use_size; + context->ustorm_st_context.common.bd_page_base_hi = U64_HI(fp->rx_desc_mapping); - context->ustorm_st_context.rx_bd_page_base_lo = + context->ustorm_st_context.common.bd_page_base_lo = U64_LO(fp->rx_desc_mapping); - context->ustorm_st_context.status_block_id = i; - context->ustorm_st_context.sb_index_number = - HC_INDEX_U_ETH_RX_CQ_CONS; - context->ustorm_st_context.rcq_base_address_hi = - U64_HI(fp->rx_comp_mapping); - context->ustorm_st_context.rcq_base_address_lo = - U64_LO(fp->rx_comp_mapping); - context->ustorm_st_context.flags = - USTORM_ETH_ST_CONTEXT_ENABLE_MC_ALIGNMENT; - context->ustorm_st_context.mc_alignment_size = 64; - context->ustorm_st_context.num_rss = bp->num_queues; - context->cstorm_st_context.sb_index_number = HC_INDEX_C_ETH_TX_CQ_CONS; - context->cstorm_st_context.status_block_id = i; + context->cstorm_st_context.status_block_id = sb_id; context->xstorm_ag_context.cdu_reserved = CDU_RSRVD_VALUE_TYPE_A(HW_CID(bp, i), @@ -3176,14 +3619,16 @@ static void bnx2x_init_context(struct bnx2x *bp) static void bnx2x_init_ind_table(struct bnx2x *bp) { - int port = bp->port; + int port = BP_PORT(bp); int i; if (!is_multi(bp)) return; + DP(NETIF_MSG_IFUP, "Initializing indirection table\n"); for (i = 0; i < TSTORM_INDIRECTION_TABLE_SIZE; i++) - REG_WR8(bp, TSTORM_INDIRECTION_TABLE_OFFSET(port) + i, + REG_WR8(bp, BAR_TSTRORM_INTMEM + + TSTORM_INDIRECTION_TABLE_OFFSET(port) + i, i % bp->num_queues); REG_WR(bp, PRS_REG_A_PRSU_20, 0xf); @@ -3191,77 +3636,74 @@ static void bnx2x_init_ind_table(struct bnx2x *bp) static void bnx2x_set_client_config(struct bnx2x *bp) { -#ifdef BCM_VLAN - int mode = bp->rx_mode; -#endif - int i, port = bp->port; struct tstorm_eth_client_config tstorm_client = {0}; + int port = BP_PORT(bp); + int i; - tstorm_client.mtu = bp->dev->mtu; + tstorm_client.mtu = bp->dev->mtu + ETH_OVREHEAD; tstorm_client.statistics_counter_id = 0; tstorm_client.config_flags = TSTORM_ETH_CLIENT_CONFIG_STATSITICS_ENABLE; #ifdef BCM_VLAN - if (mode && bp->vlgrp) { + if (bp->rx_mode && bp->vlgrp) { tstorm_client.config_flags |= TSTORM_ETH_CLIENT_CONFIG_VLAN_REMOVAL_ENABLE; DP(NETIF_MSG_IFUP, "vlan removal enabled\n"); } #endif - if (mode != BNX2X_RX_MODE_PROMISC) - tstorm_client.drop_flags = - TSTORM_ETH_CLIENT_CONFIG_DROP_MAC_ERR; for_each_queue(bp, i) { REG_WR(bp, BAR_TSTRORM_INTMEM + - TSTORM_CLIENT_CONFIG_OFFSET(port, i), + TSTORM_CLIENT_CONFIG_OFFSET(port, bp->fp[i].cl_id), ((u32 *)&tstorm_client)[0]); REG_WR(bp, BAR_TSTRORM_INTMEM + - TSTORM_CLIENT_CONFIG_OFFSET(port, i) + 4, + TSTORM_CLIENT_CONFIG_OFFSET(port, bp->fp[i].cl_id) + 4, ((u32 *)&tstorm_client)[1]); } -/* DP(NETIF_MSG_IFUP, "tstorm_client: 0x%08x 0x%08x\n", - ((u32 *)&tstorm_client)[0], ((u32 *)&tstorm_client)[1]); */ + DP(BNX2X_MSG_OFF, "tstorm_client: 0x%08x 0x%08x\n", + ((u32 *)&tstorm_client)[0], ((u32 *)&tstorm_client)[1]); } static void bnx2x_set_storm_rx_mode(struct bnx2x *bp) { - int mode = bp->rx_mode; - int port = bp->port; struct tstorm_eth_mac_filter_config tstorm_mac_filter = {0}; + int mode = bp->rx_mode; + int mask = (1 << BP_L_ID(bp)); + int func = BP_FUNC(bp); int i; DP(NETIF_MSG_RX_STATUS, "rx mode is %d\n", mode); switch (mode) { case BNX2X_RX_MODE_NONE: /* no Rx */ - tstorm_mac_filter.ucast_drop_all = 1; - tstorm_mac_filter.mcast_drop_all = 1; - tstorm_mac_filter.bcast_drop_all = 1; + tstorm_mac_filter.ucast_drop_all = mask; + tstorm_mac_filter.mcast_drop_all = mask; + tstorm_mac_filter.bcast_drop_all = mask; break; case BNX2X_RX_MODE_NORMAL: - tstorm_mac_filter.bcast_accept_all = 1; + tstorm_mac_filter.bcast_accept_all = mask; break; case BNX2X_RX_MODE_ALLMULTI: - tstorm_mac_filter.mcast_accept_all = 1; - tstorm_mac_filter.bcast_accept_all = 1; + tstorm_mac_filter.mcast_accept_all = mask; + tstorm_mac_filter.bcast_accept_all = mask; break; case BNX2X_RX_MODE_PROMISC: - tstorm_mac_filter.ucast_accept_all = 1; - tstorm_mac_filter.mcast_accept_all = 1; - tstorm_mac_filter.bcast_accept_all = 1; + tstorm_mac_filter.ucast_accept_all = mask; + tstorm_mac_filter.mcast_accept_all = mask; + tstorm_mac_filter.bcast_accept_all = mask; break; default: - BNX2X_ERR("bad rx mode (%d)\n", mode); + BNX2X_ERR("BAD rx mode (%d)\n", mode); + break; } for (i = 0; i < sizeof(struct tstorm_eth_mac_filter_config)/4; i++) { REG_WR(bp, BAR_TSTRORM_INTMEM + - TSTORM_MAC_FILTER_CONFIG_OFFSET(port) + i * 4, + TSTORM_MAC_FILTER_CONFIG_OFFSET(func) + i * 4, ((u32 *)&tstorm_mac_filter)[i]); -/* DP(NETIF_MSG_IFUP, "tstorm_mac_filter[%d]: 0x%08x\n", i, +/* DP(NETIF_MSG_IFUP, "tstorm_mac_filter[%d]: 0x%08x\n", i, ((u32 *)&tstorm_mac_filter)[i]); */ } @@ -3271,26 +3713,30 @@ static void bnx2x_set_storm_rx_mode(struct bnx2x *bp) static void bnx2x_init_internal(struct bnx2x *bp) { - int port = bp->port; struct tstorm_eth_function_common_config tstorm_config = {0}; struct stats_indication_flags stats_flags = {0}; + int port = BP_PORT(bp); + int func = BP_FUNC(bp); + int i; if (is_multi(bp)) { tstorm_config.config_flags = MULTI_FLAGS; tstorm_config.rss_result_mask = MULTI_MASK; } + tstorm_config.leading_client_id = BP_L_ID(bp); + REG_WR(bp, BAR_TSTRORM_INTMEM + - TSTORM_FUNCTION_COMMON_CONFIG_OFFSET(port), + TSTORM_FUNCTION_COMMON_CONFIG_OFFSET(func), (*(u32 *)&tstorm_config)); -/* DP(NETIF_MSG_IFUP, "tstorm_config: 0x%08x\n", +/* DP(NETIF_MSG_IFUP, "tstorm_config: 0x%08x\n", (*(u32 *)&tstorm_config)); */ bp->rx_mode = BNX2X_RX_MODE_NONE; /* no rx until link is up */ bnx2x_set_storm_rx_mode(bp); - stats_flags.collect_eth = cpu_to_le32(1); + stats_flags.collect_eth = 1; REG_WR(bp, BAR_XSTRORM_INTMEM + XSTORM_STATS_FLAGS_OFFSET(port), ((u32 *)&stats_flags)[0]); @@ -3307,8 +3753,28 @@ static void bnx2x_init_internal(struct bnx2x *bp) REG_WR(bp, BAR_CSTRORM_INTMEM + CSTORM_STATS_FLAGS_OFFSET(port) + 4, ((u32 *)&stats_flags)[1]); -/* DP(NETIF_MSG_IFUP, "stats_flags: 0x%08x 0x%08x\n", +/* DP(NETIF_MSG_IFUP, "stats_flags: 0x%08x 0x%08x\n", ((u32 *)&stats_flags)[0], ((u32 *)&stats_flags)[1]); */ + + if (CHIP_IS_E1H(bp)) { + REG_WR8(bp, BAR_XSTRORM_INTMEM + XSTORM_FUNCTION_MODE_OFFSET, + IS_E1HMF(bp)); + REG_WR8(bp, BAR_TSTRORM_INTMEM + TSTORM_FUNCTION_MODE_OFFSET, + IS_E1HMF(bp)); + REG_WR8(bp, BAR_CSTRORM_INTMEM + CSTORM_FUNCTION_MODE_OFFSET, + IS_E1HMF(bp)); + REG_WR8(bp, BAR_USTRORM_INTMEM + USTORM_FUNCTION_MODE_OFFSET, + IS_E1HMF(bp)); + + REG_WR16(bp, BAR_XSTRORM_INTMEM + + XSTORM_E1HOV_OFFSET(func), bp->e1hov); + } + + /* Zero this manualy as its initialization is + currently missing in the initTool */ + for (i = 0; i < USTORM_AGG_DATA_SIZE >> 2; i++) + REG_WR(bp, BAR_USTRORM_INTMEM + + USTORM_AGG_DATA_OFFSET + 4*i, 0); } static void bnx2x_nic_init(struct bnx2x *bp) @@ -3318,15 +3784,20 @@ static void bnx2x_nic_init(struct bnx2x *bp) for_each_queue(bp, i) { struct bnx2x_fastpath *fp = &bp->fp[i]; + fp->bp = bp; fp->state = BNX2X_FP_STATE_CLOSED; - DP(NETIF_MSG_IFUP, "bnx2x_init_sb(%p,%p,%d);\n", - bp, fp->status_blk, i); fp->index = i; - bnx2x_init_sb(bp, fp->status_blk, fp->status_blk_mapping, i); + fp->cl_id = BP_L_ID(bp) + i; + fp->sb_id = fp->cl_id; + DP(NETIF_MSG_IFUP, + "bnx2x_init_sb(%p,%p) index %d cl_id %d sb %d\n", + bp, fp->status_blk, i, FP_CL_ID(fp), FP_SB_ID(fp)); + bnx2x_init_sb(bp, FP_SB_ID(fp), fp->status_blk, + fp->status_blk_mapping); } bnx2x_init_def_sb(bp, bp->def_status_blk, - bp->def_status_blk_mapping, 0x10); + bp->def_status_blk_mapping, DEF_SB_ID); bnx2x_update_coalesce(bp); bnx2x_init_rx_rings(bp); bnx2x_init_tx_ring(bp); @@ -3336,7 +3807,6 @@ static void bnx2x_nic_init(struct bnx2x *bp) bnx2x_init_stats(bp); bnx2x_init_ind_table(bp); bnx2x_int_enable(bp); - } /* end of nic init */ @@ -3374,7 +3844,7 @@ gunzip_nomem2: gunzip_nomem1: printk(KERN_ERR PFX "%s: Cannot allocate firmware buffer for" - " uncompression\n", bp->dev->name); + " un-compression\n", bp->dev->name); return -ENOMEM; } @@ -3402,7 +3872,7 @@ static int bnx2x_gunzip(struct bnx2x *bp, u8 *zbuf, int len) n = 10; -#define FNAME 0x8 +#define FNAME 0x8 if (zbuf[3] & FNAME) while ((zbuf[n++] != 0) && (n < len)); @@ -3439,41 +3909,25 @@ static int bnx2x_gunzip(struct bnx2x *bp, u8 *zbuf, int len) /* nic load/unload */ /* - * general service functions + * General service functions */ /* send a NIG loopback debug packet */ static void bnx2x_lb_pckt(struct bnx2x *bp) { -#ifdef USE_DMAE u32 wb_write[3]; -#endif /* Ethernet source and destination addresses */ -#ifdef USE_DMAE wb_write[0] = 0x55555555; wb_write[1] = 0x55555555; - wb_write[2] = 0x20; /* SOP */ + wb_write[2] = 0x20; /* SOP */ REG_WR_DMAE(bp, NIG_REG_DEBUG_PACKET_LB, wb_write, 3); -#else - REG_WR_IND(bp, NIG_REG_DEBUG_PACKET_LB, 0x55555555); - REG_WR_IND(bp, NIG_REG_DEBUG_PACKET_LB + 4, 0x55555555); - /* SOP */ - REG_WR_IND(bp, NIG_REG_DEBUG_PACKET_LB + 8, 0x20); -#endif /* NON-IP protocol */ -#ifdef USE_DMAE wb_write[0] = 0x09000000; wb_write[1] = 0x55555555; - wb_write[2] = 0x10; /* EOP, eop_bvalid = 0 */ + wb_write[2] = 0x10; /* EOP, eop_bvalid = 0 */ REG_WR_DMAE(bp, NIG_REG_DEBUG_PACKET_LB, wb_write, 3); -#else - REG_WR_IND(bp, NIG_REG_DEBUG_PACKET_LB, 0x09000000); - REG_WR_IND(bp, NIG_REG_DEBUG_PACKET_LB + 4, 0x55555555); - /* EOP, eop_bvalid = 0 */ - REG_WR_IND(bp, NIG_REG_DEBUG_PACKET_LB + 8, 0x10); -#endif } /* some of the internal memories @@ -3511,13 +3965,9 @@ static int bnx2x_int_mem_test(struct bnx2x *bp) /* Wait until NIG register shows 1 packet of size 0x10 */ count = 1000 * factor; while (count) { -#ifdef BNX2X_DMAE_RD + bnx2x_read_dmae(bp, NIG_REG_STAT2_BRB_OCTET, 2); val = *bnx2x_sp(bp, wb_data[0]); -#else - val = REG_RD(bp, NIG_REG_STAT2_BRB_OCTET); - REG_RD(bp, NIG_REG_STAT2_BRB_OCTET + 4); -#endif if (val == 0x10) break; @@ -3533,7 +3983,6 @@ static int bnx2x_int_mem_test(struct bnx2x *bp) count = 1000 * factor; while (count) { val = REG_RD(bp, PRS_REG_NUM_OF_PACKETS); - if (val == 1) break; @@ -3546,9 +3995,9 @@ static int bnx2x_int_mem_test(struct bnx2x *bp) } /* Reset and init BRB, PRS */ - REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR, 0x3); + REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR, 0x03); msleep(50); - REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, 0x3); + REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, 0x03); msleep(50); bnx2x_init_block(bp, BRB1_COMMON_START, BRB1_COMMON_END); bnx2x_init_block(bp, PRS_COMMON_START, PRS_COMMON_END); @@ -3572,13 +4021,9 @@ static int bnx2x_int_mem_test(struct bnx2x *bp) packets of size 11*0x10 = 0xb0 */ count = 1000 * factor; while (count) { -#ifdef BNX2X_DMAE_RD + bnx2x_read_dmae(bp, NIG_REG_STAT2_BRB_OCTET, 2); val = *bnx2x_sp(bp, wb_data[0]); -#else - val = REG_RD(bp, NIG_REG_STAT2_BRB_OCTET); - REG_RD(bp, NIG_REG_STAT2_BRB_OCTET + 4); -#endif if (val == 0xb0) break; @@ -3648,85 +4093,75 @@ static void enable_blocks_attention(struct bnx2x *bp) REG_WR(bp, XSDM_REG_XSDM_INT_MASK_0, 0); REG_WR(bp, XSDM_REG_XSDM_INT_MASK_1, 0); REG_WR(bp, XCM_REG_XCM_INT_MASK, 0); -/* REG_WR(bp, XSEM_REG_XSEM_INT_MASK_0, 0); */ -/* REG_WR(bp, XSEM_REG_XSEM_INT_MASK_1, 0); */ +/* REG_WR(bp, XSEM_REG_XSEM_INT_MASK_0, 0); */ +/* REG_WR(bp, XSEM_REG_XSEM_INT_MASK_1, 0); */ REG_WR(bp, USDM_REG_USDM_INT_MASK_0, 0); REG_WR(bp, USDM_REG_USDM_INT_MASK_1, 0); REG_WR(bp, UCM_REG_UCM_INT_MASK, 0); -/* REG_WR(bp, USEM_REG_USEM_INT_MASK_0, 0); */ -/* REG_WR(bp, USEM_REG_USEM_INT_MASK_1, 0); */ +/* REG_WR(bp, USEM_REG_USEM_INT_MASK_0, 0); */ +/* REG_WR(bp, USEM_REG_USEM_INT_MASK_1, 0); */ REG_WR(bp, GRCBASE_UPB + PB_REG_PB_INT_MASK, 0); REG_WR(bp, CSDM_REG_CSDM_INT_MASK_0, 0); REG_WR(bp, CSDM_REG_CSDM_INT_MASK_1, 0); REG_WR(bp, CCM_REG_CCM_INT_MASK, 0); -/* REG_WR(bp, CSEM_REG_CSEM_INT_MASK_0, 0); */ -/* REG_WR(bp, CSEM_REG_CSEM_INT_MASK_1, 0); */ - REG_WR(bp, PXP2_REG_PXP2_INT_MASK, 0x480000); +/* REG_WR(bp, CSEM_REG_CSEM_INT_MASK_0, 0); */ +/* REG_WR(bp, CSEM_REG_CSEM_INT_MASK_1, 0); */ + if (CHIP_REV_IS_FPGA(bp)) + REG_WR(bp, PXP2_REG_PXP2_INT_MASK_0, 0x580000); + else + REG_WR(bp, PXP2_REG_PXP2_INT_MASK_0, 0x480000); REG_WR(bp, TSDM_REG_TSDM_INT_MASK_0, 0); REG_WR(bp, TSDM_REG_TSDM_INT_MASK_1, 0); REG_WR(bp, TCM_REG_TCM_INT_MASK, 0); -/* REG_WR(bp, TSEM_REG_TSEM_INT_MASK_0, 0); */ -/* REG_WR(bp, TSEM_REG_TSEM_INT_MASK_1, 0); */ +/* REG_WR(bp, TSEM_REG_TSEM_INT_MASK_0, 0); */ +/* REG_WR(bp, TSEM_REG_TSEM_INT_MASK_1, 0); */ REG_WR(bp, CDU_REG_CDU_INT_MASK, 0); REG_WR(bp, DMAE_REG_DMAE_INT_MASK, 0); -/* REG_WR(bp, MISC_REG_MISC_INT_MASK, 0); */ - REG_WR(bp, PBF_REG_PBF_INT_MASK, 0X18); /* bit 3,4 masked */ +/* REG_WR(bp, MISC_REG_MISC_INT_MASK, 0); */ + REG_WR(bp, PBF_REG_PBF_INT_MASK, 0X18); /* bit 3,4 masked */ } -static int bnx2x_function_init(struct bnx2x *bp, int mode) + +static int bnx2x_init_common(struct bnx2x *bp) { - int func = bp->port; - int port = func ? PORT1 : PORT0; u32 val, i; -#ifdef USE_DMAE - u32 wb_write[2]; -#endif - - DP(BNX2X_MSG_MCP, "function is %d mode is %x\n", func, mode); - if ((func != 0) && (func != 1)) { - BNX2X_ERR("BAD function number (%d)\n", func); - return -ENODEV; - } - bnx2x_gunzip_init(bp); + DP(BNX2X_MSG_MCP, "starting common init func %d\n", BP_FUNC(bp)); - if (mode & 0x1) { /* init common */ - DP(BNX2X_MSG_MCP, "starting common init func %d mode %x\n", - func, mode); - REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, - 0xffffffff); - REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET, - 0xfffc); - bnx2x_init_block(bp, MISC_COMMON_START, MISC_COMMON_END); + REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, 0xffffffff); + REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET, 0xfffc); - REG_WR(bp, MISC_REG_LCPLL_CTRL_REG_2, 0x100); - msleep(30); - REG_WR(bp, MISC_REG_LCPLL_CTRL_REG_2, 0x0); + bnx2x_init_block(bp, MISC_COMMON_START, MISC_COMMON_END); + if (CHIP_IS_E1H(bp)) + REG_WR(bp, MISC_REG_E1HMF_MODE, IS_E1HMF(bp)); - bnx2x_init_block(bp, PXP_COMMON_START, PXP_COMMON_END); - bnx2x_init_block(bp, PXP2_COMMON_START, PXP2_COMMON_END); + REG_WR(bp, MISC_REG_LCPLL_CTRL_REG_2, 0x100); + msleep(30); + REG_WR(bp, MISC_REG_LCPLL_CTRL_REG_2, 0x0); - bnx2x_init_pxp(bp); + bnx2x_init_block(bp, PXP_COMMON_START, PXP_COMMON_END); + if (CHIP_IS_E1(bp)) { + /* enable HW interrupt from PXP on USDM overflow + bit 16 on INT_MASK_0 */ + REG_WR(bp, PXP_REG_PXP_INT_MASK_0, 0); + } - if (CHIP_REV(bp) == CHIP_REV_Ax) { - /* enable HW interrupt from PXP on USDM - overflow bit 16 on INT_MASK_0 */ - REG_WR(bp, PXP_REG_PXP_INT_MASK_0, 0); - } + bnx2x_init_block(bp, PXP2_COMMON_START, PXP2_COMMON_END); + bnx2x_init_pxp(bp); #ifdef __BIG_ENDIAN - REG_WR(bp, PXP2_REG_RQ_QM_ENDIAN_M, 1); - REG_WR(bp, PXP2_REG_RQ_TM_ENDIAN_M, 1); - REG_WR(bp, PXP2_REG_RQ_SRC_ENDIAN_M, 1); - REG_WR(bp, PXP2_REG_RQ_CDU_ENDIAN_M, 1); - REG_WR(bp, PXP2_REG_RQ_DBG_ENDIAN_M, 1); - REG_WR(bp, PXP2_REG_RQ_HC_ENDIAN_M, 1); - -/* REG_WR(bp, PXP2_REG_RD_PBF_SWAP_MODE, 1); */ - REG_WR(bp, PXP2_REG_RD_QM_SWAP_MODE, 1); - REG_WR(bp, PXP2_REG_RD_TM_SWAP_MODE, 1); - REG_WR(bp, PXP2_REG_RD_SRC_SWAP_MODE, 1); - REG_WR(bp, PXP2_REG_RD_CDURD_SWAP_MODE, 1); + REG_WR(bp, PXP2_REG_RQ_QM_ENDIAN_M, 1); + REG_WR(bp, PXP2_REG_RQ_TM_ENDIAN_M, 1); + REG_WR(bp, PXP2_REG_RQ_SRC_ENDIAN_M, 1); + REG_WR(bp, PXP2_REG_RQ_CDU_ENDIAN_M, 1); + REG_WR(bp, PXP2_REG_RQ_DBG_ENDIAN_M, 1); + REG_WR(bp, PXP2_REG_RQ_HC_ENDIAN_M, 1); + +/* REG_WR(bp, PXP2_REG_RD_PBF_SWAP_MODE, 1); */ + REG_WR(bp, PXP2_REG_RD_QM_SWAP_MODE, 1); + REG_WR(bp, PXP2_REG_RD_TM_SWAP_MODE, 1); + REG_WR(bp, PXP2_REG_RD_SRC_SWAP_MODE, 1); + REG_WR(bp, PXP2_REG_RD_CDURD_SWAP_MODE, 1); #endif #ifndef BCM_ISCSI @@ -3734,92 +4169,105 @@ static int bnx2x_function_init(struct bnx2x *bp, int mode) REG_WR(bp, PRS_REG_NIC_MODE, 1); #endif - REG_WR(bp, PXP2_REG_RQ_CDU_P_SIZE, 5); + REG_WR(bp, PXP2_REG_RQ_CDU_P_SIZE, 2); #ifdef BCM_ISCSI - REG_WR(bp, PXP2_REG_RQ_TM_P_SIZE, 5); - REG_WR(bp, PXP2_REG_RQ_QM_P_SIZE, 5); - REG_WR(bp, PXP2_REG_RQ_SRC_P_SIZE, 5); + REG_WR(bp, PXP2_REG_RQ_TM_P_SIZE, 5); + REG_WR(bp, PXP2_REG_RQ_QM_P_SIZE, 5); + REG_WR(bp, PXP2_REG_RQ_SRC_P_SIZE, 5); #endif - bnx2x_init_block(bp, DMAE_COMMON_START, DMAE_COMMON_END); + if (CHIP_REV_IS_FPGA(bp) && CHIP_IS_E1H(bp)) + REG_WR(bp, PXP2_REG_PGL_TAGS_LIMIT, 0x1); - /* let the HW do it's magic ... */ - msleep(100); - /* finish PXP init - (can be moved up if we want to use the DMAE) */ - val = REG_RD(bp, PXP2_REG_RQ_CFG_DONE); - if (val != 1) { - BNX2X_ERR("PXP2 CFG failed\n"); - return -EBUSY; - } + /* let the HW do it's magic ... */ + msleep(100); + /* finish PXP init */ + val = REG_RD(bp, PXP2_REG_RQ_CFG_DONE); + if (val != 1) { + BNX2X_ERR("PXP2 CFG failed\n"); + return -EBUSY; + } + val = REG_RD(bp, PXP2_REG_RD_INIT_DONE); + if (val != 1) { + BNX2X_ERR("PXP2 RD_INIT failed\n"); + return -EBUSY; + } - val = REG_RD(bp, PXP2_REG_RD_INIT_DONE); - if (val != 1) { - BNX2X_ERR("PXP2 RD_INIT failed\n"); - return -EBUSY; - } + REG_WR(bp, PXP2_REG_RQ_DISABLE_INPUTS, 0); + REG_WR(bp, PXP2_REG_RD_DISABLE_INPUTS, 0); - REG_WR(bp, PXP2_REG_RQ_DISABLE_INPUTS, 0); - REG_WR(bp, PXP2_REG_RD_DISABLE_INPUTS, 0); + bnx2x_init_block(bp, DMAE_COMMON_START, DMAE_COMMON_END); - bnx2x_init_fill(bp, TSEM_REG_PRAM, 0, 8); + /* clean the DMAE memory */ + bp->dmae_ready = 1; + bnx2x_init_fill(bp, TSEM_REG_PRAM, 0, 8); - bnx2x_init_block(bp, TCM_COMMON_START, TCM_COMMON_END); - bnx2x_init_block(bp, UCM_COMMON_START, UCM_COMMON_END); - bnx2x_init_block(bp, CCM_COMMON_START, CCM_COMMON_END); - bnx2x_init_block(bp, XCM_COMMON_START, XCM_COMMON_END); + bnx2x_init_block(bp, TCM_COMMON_START, TCM_COMMON_END); + bnx2x_init_block(bp, UCM_COMMON_START, UCM_COMMON_END); + bnx2x_init_block(bp, CCM_COMMON_START, CCM_COMMON_END); + bnx2x_init_block(bp, XCM_COMMON_START, XCM_COMMON_END); -#ifdef BNX2X_DMAE_RD - bnx2x_read_dmae(bp, XSEM_REG_PASSIVE_BUFFER, 3); - bnx2x_read_dmae(bp, CSEM_REG_PASSIVE_BUFFER, 3); - bnx2x_read_dmae(bp, TSEM_REG_PASSIVE_BUFFER, 3); - bnx2x_read_dmae(bp, USEM_REG_PASSIVE_BUFFER, 3); -#else - REG_RD(bp, XSEM_REG_PASSIVE_BUFFER); - REG_RD(bp, XSEM_REG_PASSIVE_BUFFER + 4); - REG_RD(bp, XSEM_REG_PASSIVE_BUFFER + 8); - REG_RD(bp, CSEM_REG_PASSIVE_BUFFER); - REG_RD(bp, CSEM_REG_PASSIVE_BUFFER + 4); - REG_RD(bp, CSEM_REG_PASSIVE_BUFFER + 8); - REG_RD(bp, TSEM_REG_PASSIVE_BUFFER); - REG_RD(bp, TSEM_REG_PASSIVE_BUFFER + 4); - REG_RD(bp, TSEM_REG_PASSIVE_BUFFER + 8); - REG_RD(bp, USEM_REG_PASSIVE_BUFFER); - REG_RD(bp, USEM_REG_PASSIVE_BUFFER + 4); - REG_RD(bp, USEM_REG_PASSIVE_BUFFER + 8); -#endif - bnx2x_init_block(bp, QM_COMMON_START, QM_COMMON_END); - /* soft reset pulse */ - REG_WR(bp, QM_REG_SOFT_RESET, 1); - REG_WR(bp, QM_REG_SOFT_RESET, 0); + bnx2x_read_dmae(bp, XSEM_REG_PASSIVE_BUFFER, 3); + bnx2x_read_dmae(bp, CSEM_REG_PASSIVE_BUFFER, 3); + bnx2x_read_dmae(bp, TSEM_REG_PASSIVE_BUFFER, 3); + bnx2x_read_dmae(bp, USEM_REG_PASSIVE_BUFFER, 3); + + bnx2x_init_block(bp, QM_COMMON_START, QM_COMMON_END); + /* soft reset pulse */ + REG_WR(bp, QM_REG_SOFT_RESET, 1); + REG_WR(bp, QM_REG_SOFT_RESET, 0); #ifdef BCM_ISCSI - bnx2x_init_block(bp, TIMERS_COMMON_START, TIMERS_COMMON_END); + bnx2x_init_block(bp, TIMERS_COMMON_START, TIMERS_COMMON_END); #endif - bnx2x_init_block(bp, DQ_COMMON_START, DQ_COMMON_END); - REG_WR(bp, DORQ_REG_DPM_CID_OFST, BCM_PAGE_BITS); - if (CHIP_REV(bp) == CHIP_REV_Ax) { - /* enable hw interrupt from doorbell Q */ - REG_WR(bp, DORQ_REG_DORQ_INT_MASK, 0); - } - bnx2x_init_block(bp, BRB1_COMMON_START, BRB1_COMMON_END); + bnx2x_init_block(bp, DQ_COMMON_START, DQ_COMMON_END); + REG_WR(bp, DORQ_REG_DPM_CID_OFST, BCM_PAGE_SHIFT); + if (!CHIP_REV_IS_SLOW(bp)) { + /* enable hw interrupt from doorbell Q */ + REG_WR(bp, DORQ_REG_DORQ_INT_MASK, 0); + } - if (CHIP_REV_IS_SLOW(bp)) { - /* fix for emulation and FPGA for no pause */ - REG_WR(bp, BRB1_REG_PAUSE_HIGH_THRESHOLD_0, 513); - REG_WR(bp, BRB1_REG_PAUSE_HIGH_THRESHOLD_1, 513); - REG_WR(bp, BRB1_REG_PAUSE_LOW_THRESHOLD_0, 0); - REG_WR(bp, BRB1_REG_PAUSE_LOW_THRESHOLD_1, 0); - } + bnx2x_init_block(bp, BRB1_COMMON_START, BRB1_COMMON_END); + if (CHIP_REV_IS_SLOW(bp)) { + /* fix for emulation and FPGA for no pause */ + REG_WR(bp, BRB1_REG_PAUSE_HIGH_THRESHOLD_0, 513); + REG_WR(bp, BRB1_REG_PAUSE_HIGH_THRESHOLD_1, 513); + REG_WR(bp, BRB1_REG_PAUSE_LOW_THRESHOLD_0, 0); + REG_WR(bp, BRB1_REG_PAUSE_LOW_THRESHOLD_1, 0); + } - bnx2x_init_block(bp, PRS_COMMON_START, PRS_COMMON_END); + bnx2x_init_block(bp, PRS_COMMON_START, PRS_COMMON_END); + if (CHIP_IS_E1H(bp)) + REG_WR(bp, PRS_REG_E1HOV_MODE, IS_E1HMF(bp)); - bnx2x_init_block(bp, TSDM_COMMON_START, TSDM_COMMON_END); - bnx2x_init_block(bp, CSDM_COMMON_START, CSDM_COMMON_END); - bnx2x_init_block(bp, USDM_COMMON_START, USDM_COMMON_END); - bnx2x_init_block(bp, XSDM_COMMON_START, XSDM_COMMON_END); + bnx2x_init_block(bp, TSDM_COMMON_START, TSDM_COMMON_END); + bnx2x_init_block(bp, CSDM_COMMON_START, CSDM_COMMON_END); + bnx2x_init_block(bp, USDM_COMMON_START, USDM_COMMON_END); + bnx2x_init_block(bp, XSDM_COMMON_START, XSDM_COMMON_END); + if (CHIP_IS_E1H(bp)) { + bnx2x_init_fill(bp, TSTORM_INTMEM_ADDR, 0, + STORM_INTMEM_SIZE_E1H/2); + bnx2x_init_fill(bp, + TSTORM_INTMEM_ADDR + STORM_INTMEM_SIZE_E1H/2, + 0, STORM_INTMEM_SIZE_E1H/2); + bnx2x_init_fill(bp, CSTORM_INTMEM_ADDR, 0, + STORM_INTMEM_SIZE_E1H/2); + bnx2x_init_fill(bp, + CSTORM_INTMEM_ADDR + STORM_INTMEM_SIZE_E1H/2, + 0, STORM_INTMEM_SIZE_E1H/2); + bnx2x_init_fill(bp, XSTORM_INTMEM_ADDR, 0, + STORM_INTMEM_SIZE_E1H/2); + bnx2x_init_fill(bp, + XSTORM_INTMEM_ADDR + STORM_INTMEM_SIZE_E1H/2, + 0, STORM_INTMEM_SIZE_E1H/2); + bnx2x_init_fill(bp, USTORM_INTMEM_ADDR, 0, + STORM_INTMEM_SIZE_E1H/2); + bnx2x_init_fill(bp, + USTORM_INTMEM_ADDR + STORM_INTMEM_SIZE_E1H/2, + 0, STORM_INTMEM_SIZE_E1H/2); + } else { /* E1 */ bnx2x_init_fill(bp, TSTORM_INTMEM_ADDR, 0, STORM_INTMEM_SIZE_E1); bnx2x_init_fill(bp, CSTORM_INTMEM_ADDR, 0, @@ -3828,157 +4276,141 @@ static int bnx2x_function_init(struct bnx2x *bp, int mode) STORM_INTMEM_SIZE_E1); bnx2x_init_fill(bp, USTORM_INTMEM_ADDR, 0, STORM_INTMEM_SIZE_E1); + } - bnx2x_init_block(bp, TSEM_COMMON_START, TSEM_COMMON_END); - bnx2x_init_block(bp, USEM_COMMON_START, USEM_COMMON_END); - bnx2x_init_block(bp, CSEM_COMMON_START, CSEM_COMMON_END); - bnx2x_init_block(bp, XSEM_COMMON_START, XSEM_COMMON_END); - - /* sync semi rtc */ - REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR, - 0x80000000); - REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, - 0x80000000); - - bnx2x_init_block(bp, UPB_COMMON_START, UPB_COMMON_END); - bnx2x_init_block(bp, XPB_COMMON_START, XPB_COMMON_END); - bnx2x_init_block(bp, PBF_COMMON_START, PBF_COMMON_END); - - REG_WR(bp, SRC_REG_SOFT_RST, 1); - for (i = SRC_REG_KEYRSS0_0; i <= SRC_REG_KEYRSS1_9; i += 4) { - REG_WR(bp, i, 0xc0cac01a); - /* TODO: replace with something meaningful */ - } - /* SRCH COMMON comes here */ - REG_WR(bp, SRC_REG_SOFT_RST, 0); - - if (sizeof(union cdu_context) != 1024) { - /* we currently assume that a context is 1024 bytes */ - printk(KERN_ALERT PFX "please adjust the size of" - " cdu_context(%ld)\n", - (long)sizeof(union cdu_context)); - } - val = (4 << 24) + (0 << 12) + 1024; - REG_WR(bp, CDU_REG_CDU_GLOBAL_PARAMS, val); - bnx2x_init_block(bp, CDU_COMMON_START, CDU_COMMON_END); - - bnx2x_init_block(bp, CFC_COMMON_START, CFC_COMMON_END); - REG_WR(bp, CFC_REG_INIT_REG, 0x7FF); - - bnx2x_init_block(bp, HC_COMMON_START, HC_COMMON_END); - bnx2x_init_block(bp, MISC_AEU_COMMON_START, - MISC_AEU_COMMON_END); - /* RXPCS COMMON comes here */ - /* EMAC0 COMMON comes here */ - /* EMAC1 COMMON comes here */ - /* DBU COMMON comes here */ - /* DBG COMMON comes here */ - bnx2x_init_block(bp, NIG_COMMON_START, NIG_COMMON_END); + bnx2x_init_block(bp, TSEM_COMMON_START, TSEM_COMMON_END); + bnx2x_init_block(bp, USEM_COMMON_START, USEM_COMMON_END); + bnx2x_init_block(bp, CSEM_COMMON_START, CSEM_COMMON_END); + bnx2x_init_block(bp, XSEM_COMMON_START, XSEM_COMMON_END); - if (CHIP_REV_IS_SLOW(bp)) - msleep(200); + /* sync semi rtc */ + REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR, + 0x80000000); + REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, + 0x80000000); - /* finish CFC init */ - val = REG_RD(bp, CFC_REG_LL_INIT_DONE); - if (val != 1) { - BNX2X_ERR("CFC LL_INIT failed\n"); - return -EBUSY; - } + bnx2x_init_block(bp, UPB_COMMON_START, UPB_COMMON_END); + bnx2x_init_block(bp, XPB_COMMON_START, XPB_COMMON_END); + bnx2x_init_block(bp, PBF_COMMON_START, PBF_COMMON_END); - val = REG_RD(bp, CFC_REG_AC_INIT_DONE); - if (val != 1) { - BNX2X_ERR("CFC AC_INIT failed\n"); - return -EBUSY; - } + REG_WR(bp, SRC_REG_SOFT_RST, 1); + for (i = SRC_REG_KEYRSS0_0; i <= SRC_REG_KEYRSS1_9; i += 4) { + REG_WR(bp, i, 0xc0cac01a); + /* TODO: replace with something meaningful */ + } + if (CHIP_IS_E1H(bp)) + bnx2x_init_block(bp, SRCH_COMMON_START, SRCH_COMMON_END); + REG_WR(bp, SRC_REG_SOFT_RST, 0); - val = REG_RD(bp, CFC_REG_CAM_INIT_DONE); - if (val != 1) { - BNX2X_ERR("CFC CAM_INIT failed\n"); - return -EBUSY; - } + if (sizeof(union cdu_context) != 1024) + /* we currently assume that a context is 1024 bytes */ + printk(KERN_ALERT PFX "please adjust the size of" + " cdu_context(%ld)\n", (long)sizeof(union cdu_context)); - REG_WR(bp, CFC_REG_DEBUG0, 0); + bnx2x_init_block(bp, CDU_COMMON_START, CDU_COMMON_END); + val = (4 << 24) + (0 << 12) + 1024; + REG_WR(bp, CDU_REG_CDU_GLOBAL_PARAMS, val); + if (CHIP_IS_E1(bp)) { + /* !!! fix pxp client crdit until excel update */ + REG_WR(bp, CDU_REG_CDU_DEBUG, 0x264); + REG_WR(bp, CDU_REG_CDU_DEBUG, 0); + } - /* read NIG statistic - to see if this is our first up since powerup */ -#ifdef BNX2X_DMAE_RD - bnx2x_read_dmae(bp, NIG_REG_STAT2_BRB_OCTET, 2); - val = *bnx2x_sp(bp, wb_data[0]); -#else - val = REG_RD(bp, NIG_REG_STAT2_BRB_OCTET); - REG_RD(bp, NIG_REG_STAT2_BRB_OCTET + 4); -#endif - /* do internal memory self test */ - if ((val == 0) && bnx2x_int_mem_test(bp)) { - BNX2X_ERR("internal mem selftest failed\n"); - return -EBUSY; - } + bnx2x_init_block(bp, CFC_COMMON_START, CFC_COMMON_END); + REG_WR(bp, CFC_REG_INIT_REG, 0x7FF); - /* clear PXP2 attentions */ - REG_RD(bp, PXP2_REG_PXP2_INT_STS_CLR); + bnx2x_init_block(bp, HC_COMMON_START, HC_COMMON_END); + bnx2x_init_block(bp, MISC_AEU_COMMON_START, MISC_AEU_COMMON_END); - enable_blocks_attention(bp); - /* enable_blocks_parity(bp); */ + /* PXPCS COMMON comes here */ + /* Reset PCIE errors for debug */ + REG_WR(bp, 0x2814, 0xffffffff); + REG_WR(bp, 0x3820, 0xffffffff); - switch (bp->board & SHARED_HW_CFG_BOARD_TYPE_MASK) { - case SHARED_HW_CFG_BOARD_TYPE_BCM957710A1022G: - /* Fan failure is indicated by SPIO 5 */ - bnx2x_set_spio(bp, MISC_REGISTERS_SPIO_5, - MISC_REGISTERS_SPIO_INPUT_HI_Z); + /* EMAC0 COMMON comes here */ + /* EMAC1 COMMON comes here */ + /* DBU COMMON comes here */ + /* DBG COMMON comes here */ + + bnx2x_init_block(bp, NIG_COMMON_START, NIG_COMMON_END); + if (CHIP_IS_E1H(bp)) { + REG_WR(bp, NIG_REG_LLH_MF_MODE, IS_E1HMF(bp)); + REG_WR(bp, NIG_REG_LLH_E1HOV_MODE, IS_E1HMF(bp)); + } + + if (CHIP_REV_IS_SLOW(bp)) + msleep(200); + + /* finish CFC init */ + val = reg_poll(bp, CFC_REG_LL_INIT_DONE, 1, 100, 10); + if (val != 1) { + BNX2X_ERR("CFC LL_INIT failed\n"); + return -EBUSY; + } + val = reg_poll(bp, CFC_REG_AC_INIT_DONE, 1, 100, 10); + if (val != 1) { + BNX2X_ERR("CFC AC_INIT failed\n"); + return -EBUSY; + } + val = reg_poll(bp, CFC_REG_CAM_INIT_DONE, 1, 100, 10); + if (val != 1) { + BNX2X_ERR("CFC CAM_INIT failed\n"); + return -EBUSY; + } + REG_WR(bp, CFC_REG_DEBUG0, 0); - /* set to active low mode */ - val = REG_RD(bp, MISC_REG_SPIO_INT); - val |= ((1 << MISC_REGISTERS_SPIO_5) << + /* read NIG statistic + to see if this is our first up since powerup */ + bnx2x_read_dmae(bp, NIG_REG_STAT2_BRB_OCTET, 2); + val = *bnx2x_sp(bp, wb_data[0]); + + /* do internal memory self test */ + if ((CHIP_IS_E1(bp)) && (val == 0) && bnx2x_int_mem_test(bp)) { + BNX2X_ERR("internal mem self test failed\n"); + return -EBUSY; + } + + switch (bp->common.board & SHARED_HW_CFG_BOARD_TYPE_MASK) { + case SHARED_HW_CFG_BOARD_TYPE_BCM957710A1022G: + /* Fan failure is indicated by SPIO 5 */ + bnx2x_set_spio(bp, MISC_REGISTERS_SPIO_5, + MISC_REGISTERS_SPIO_INPUT_HI_Z); + + /* set to active low mode */ + val = REG_RD(bp, MISC_REG_SPIO_INT); + val |= ((1 << MISC_REGISTERS_SPIO_5) << MISC_REGISTERS_SPIO_INT_OLD_SET_POS); - REG_WR(bp, MISC_REG_SPIO_INT, val); + REG_WR(bp, MISC_REG_SPIO_INT, val); - /* enable interrupt to signal the IGU */ - val = REG_RD(bp, MISC_REG_SPIO_EVENT_EN); - val |= (1 << MISC_REGISTERS_SPIO_5); - REG_WR(bp, MISC_REG_SPIO_EVENT_EN, val); - break; + /* enable interrupt to signal the IGU */ + val = REG_RD(bp, MISC_REG_SPIO_EVENT_EN); + val |= (1 << MISC_REGISTERS_SPIO_5); + REG_WR(bp, MISC_REG_SPIO_EVENT_EN, val); + break; - default: - break; - } + default: + break; + } - } /* end of common init */ + /* clear PXP2 attentions */ + REG_RD(bp, PXP2_REG_PXP2_INT_STS_CLR_0); - /* per port init */ + enable_blocks_attention(bp); - /* the phys address is shifted right 12 bits and has an added - 1=valid bit added to the 53rd bit - then since this is a wide register(TM) - we split it into two 32 bit writes - */ -#define RQ_ONCHIP_AT_PORT_SIZE 384 -#define ONCHIP_ADDR1(x) ((u32)(((u64)x >> 12) & 0xFFFFFFFF)) -#define ONCHIP_ADDR2(x) ((u32)((1 << 20) | ((u64)x >> 44))) -#define PXP_ONE_ILT(x) ((x << 10) | x) + return 0; +} - DP(BNX2X_MSG_MCP, "starting per-function init port is %x\n", func); +static int bnx2x_init_port(struct bnx2x *bp) +{ + int port = BP_PORT(bp); + u32 val; - REG_WR(bp, NIG_REG_MASK_INTERRUPT_PORT0 + func*4, 0); + DP(BNX2X_MSG_MCP, "starting port init port %x\n", port); + + REG_WR(bp, NIG_REG_MASK_INTERRUPT_PORT0 + port*4, 0); /* Port PXP comes here */ /* Port PXP2 comes here */ - - /* Offset is - * Port0 0 - * Port1 384 */ - i = func * RQ_ONCHIP_AT_PORT_SIZE; -#ifdef USE_DMAE - wb_write[0] = ONCHIP_ADDR1(bnx2x_sp_mapping(bp, context)); - wb_write[1] = ONCHIP_ADDR2(bnx2x_sp_mapping(bp, context)); - REG_WR_DMAE(bp, PXP2_REG_RQ_ONCHIP_AT + i*8, wb_write, 2); -#else - REG_WR_IND(bp, PXP2_REG_RQ_ONCHIP_AT + i*8, - ONCHIP_ADDR1(bnx2x_sp_mapping(bp, context))); - REG_WR_IND(bp, PXP2_REG_RQ_ONCHIP_AT + i*8 + 4, - ONCHIP_ADDR2(bnx2x_sp_mapping(bp, context))); -#endif - REG_WR(bp, PXP2_REG_PSWRQ_CDU0_L2P + func*4, PXP_ONE_ILT(i)); - #ifdef BCM_ISCSI /* Port0 1 * Port1 385 */ @@ -4004,30 +4436,9 @@ static int bnx2x_function_init(struct bnx2x *bp, int mode) REG_WR_DMAE(bp, PXP2_REG_RQ_ONCHIP_AT + i*8, wb_write, 2); REG_WR(bp, PXP2_REG_PSWRQ_SRC0_L2P + func*4, PXP_ONE_ILT(i)); #endif - - /* Port TCM comes here */ - /* Port UCM comes here */ - /* Port CCM comes here */ - bnx2x_init_block(bp, func ? XCM_PORT1_START : XCM_PORT0_START, - func ? XCM_PORT1_END : XCM_PORT0_END); - -#ifdef USE_DMAE - wb_write[0] = 0; - wb_write[1] = 0; -#endif - for (i = 0; i < 32; i++) { - REG_WR(bp, QM_REG_BASEADDR + (func*32 + i)*4, 1024 * 4 * i); -#ifdef USE_DMAE - REG_WR_DMAE(bp, QM_REG_PTRTBL + (func*32 + i)*8, wb_write, 2); -#else - REG_WR_IND(bp, QM_REG_PTRTBL + (func*32 + i)*8, 0); - REG_WR_IND(bp, QM_REG_PTRTBL + (func*32 + i)*8 + 4, 0); -#endif - } - REG_WR(bp, QM_REG_CONNNUM_0 + func*4, 1024/16 - 1); + /* Port CMs come here */ /* Port QM comes here */ - #ifdef BCM_ISCSI REG_WR(bp, TM_REG_LIN0_SCAN_TIME + func*4, 1024/64*20); REG_WR(bp, TM_REG_LIN0_MAX_ACTIVE_CID + func*4, 31); @@ -4042,31 +4453,32 @@ static int bnx2x_function_init(struct bnx2x *bp, int mode) /* Port CSDM comes here */ /* Port USDM comes here */ /* Port XSDM comes here */ - bnx2x_init_block(bp, func ? TSEM_PORT1_START : TSEM_PORT0_START, - func ? TSEM_PORT1_END : TSEM_PORT0_END); - bnx2x_init_block(bp, func ? USEM_PORT1_START : USEM_PORT0_START, - func ? USEM_PORT1_END : USEM_PORT0_END); - bnx2x_init_block(bp, func ? CSEM_PORT1_START : CSEM_PORT0_START, - func ? CSEM_PORT1_END : CSEM_PORT0_END); - bnx2x_init_block(bp, func ? XSEM_PORT1_START : XSEM_PORT0_START, - func ? XSEM_PORT1_END : XSEM_PORT0_END); + bnx2x_init_block(bp, port ? TSEM_PORT1_START : TSEM_PORT0_START, + port ? TSEM_PORT1_END : TSEM_PORT0_END); + bnx2x_init_block(bp, port ? USEM_PORT1_START : USEM_PORT0_START, + port ? USEM_PORT1_END : USEM_PORT0_END); + bnx2x_init_block(bp, port ? CSEM_PORT1_START : CSEM_PORT0_START, + port ? CSEM_PORT1_END : CSEM_PORT0_END); + bnx2x_init_block(bp, port ? XSEM_PORT1_START : XSEM_PORT0_START, + port ? XSEM_PORT1_END : XSEM_PORT0_END); /* Port UPB comes here */ - /* Port XSDM comes here */ - bnx2x_init_block(bp, func ? PBF_PORT1_START : PBF_PORT0_START, - func ? PBF_PORT1_END : PBF_PORT0_END); + /* Port XPB comes here */ + + bnx2x_init_block(bp, port ? PBF_PORT1_START : PBF_PORT0_START, + port ? PBF_PORT1_END : PBF_PORT0_END); /* configure PBF to work without PAUSE mtu 9000 */ - REG_WR(bp, PBF_REG_P0_PAUSE_ENABLE + func*4, 0); + REG_WR(bp, PBF_REG_P0_PAUSE_ENABLE + port*4, 0); /* update threshold */ - REG_WR(bp, PBF_REG_P0_ARB_THRSH + func*4, (9040/16)); + REG_WR(bp, PBF_REG_P0_ARB_THRSH + port*4, (9040/16)); /* update init credit */ - REG_WR(bp, PBF_REG_P0_INIT_CRD + func*4, (9040/16) + 553 - 22); + REG_WR(bp, PBF_REG_P0_INIT_CRD + port*4, (9040/16) + 553 - 22); /* probe changes */ - REG_WR(bp, PBF_REG_INIT_P0 + func*4, 1); + REG_WR(bp, PBF_REG_INIT_P0 + port*4, 1); msleep(5); - REG_WR(bp, PBF_REG_INIT_P0 + func*4, 0); + REG_WR(bp, PBF_REG_INIT_P0 + port*4, 0); #ifdef BCM_ISCSI /* tell the searcher where the T2 table is */ @@ -4084,23 +4496,57 @@ static int bnx2x_function_init(struct bnx2x *bp, int mode) #endif /* Port CDU comes here */ /* Port CFC comes here */ - bnx2x_init_block(bp, func ? HC_PORT1_START : HC_PORT0_START, - func ? HC_PORT1_END : HC_PORT0_END); - bnx2x_init_block(bp, func ? MISC_AEU_PORT1_START : + + if (CHIP_IS_E1(bp)) { + REG_WR(bp, HC_REG_LEADING_EDGE_0 + port*8, 0); + REG_WR(bp, HC_REG_TRAILING_EDGE_0 + port*8, 0); + } + bnx2x_init_block(bp, port ? HC_PORT1_START : HC_PORT0_START, + port ? HC_PORT1_END : HC_PORT0_END); + + bnx2x_init_block(bp, port ? MISC_AEU_PORT1_START : MISC_AEU_PORT0_START, - func ? MISC_AEU_PORT1_END : MISC_AEU_PORT0_END); + port ? MISC_AEU_PORT1_END : MISC_AEU_PORT0_END); + /* init aeu_mask_attn_func_0/1: + * - SF mode: bits 3-7 are masked. only bits 0-2 are in use + * - MF mode: bit 3 is masked. bits 0-2 are in use as in SF + * bits 4-7 are used for "per vn group attention" */ + REG_WR(bp, MISC_REG_AEU_MASK_ATTN_FUNC_0 + port*4, + (IS_E1HMF(bp) ? 0xF7 : 0x7)); + /* Port PXPCS comes here */ /* Port EMAC0 comes here */ /* Port EMAC1 comes here */ /* Port DBU comes here */ /* Port DBG comes here */ - bnx2x_init_block(bp, func ? NIG_PORT1_START : NIG_PORT0_START, - func ? NIG_PORT1_END : NIG_PORT0_END); - REG_WR(bp, NIG_REG_XGXS_SERDES0_MODE_SEL + func*4, 1); + bnx2x_init_block(bp, port ? NIG_PORT1_START : NIG_PORT0_START, + port ? NIG_PORT1_END : NIG_PORT0_END); + + REG_WR(bp, NIG_REG_XGXS_SERDES0_MODE_SEL + port*4, 1); + + if (CHIP_IS_E1H(bp)) { + u32 wsum; + struct cmng_struct_per_port m_cmng_port; + int vn; + + /* 0x2 disable e1hov, 0x1 enable */ + REG_WR(bp, NIG_REG_LLH0_BRB1_DRV_MASK_MF + port*4, + (IS_E1HMF(bp) ? 0x1 : 0x2)); + + /* Init RATE SHAPING and FAIRNESS contexts. + Initialize as if there is 10G link. */ + wsum = bnx2x_calc_vn_wsum(bp); + bnx2x_init_port_minmax(bp, (int)wsum, 10000, &m_cmng_port); + if (IS_E1HMF(bp)) + for (vn = VN_0; vn < E1HVN_MAX; vn++) + bnx2x_init_vn_minmax(bp, 2*vn + port, + wsum, 10000, &m_cmng_port); + } + /* Port MCP comes here */ /* Port DMAE comes here */ - switch (bp->board & SHARED_HW_CFG_BOARD_TYPE_MASK) { + switch (bp->common.board & SHARED_HW_CFG_BOARD_TYPE_MASK) { case SHARED_HW_CFG_BOARD_TYPE_BCM957710A1022G: /* add SPIO 5 to group 0 */ val = REG_RD(bp, MISC_REG_AEU_ENABLE1_FUNC_0_OUT_0); @@ -4114,48 +4560,150 @@ static int bnx2x_function_init(struct bnx2x *bp, int mode) bnx2x__link_reset(bp); + return 0; +} + +#define ILT_PER_FUNC (768/2) +#define FUNC_ILT_BASE(func) (func * ILT_PER_FUNC) +/* the phys address is shifted right 12 bits and has an added + 1=valid bit added to the 53rd bit + then since this is a wide register(TM) + we split it into two 32 bit writes + */ +#define ONCHIP_ADDR1(x) ((u32)(((u64)x >> 12) & 0xFFFFFFFF)) +#define ONCHIP_ADDR2(x) ((u32)((1 << 20) | ((u64)x >> 44))) +#define PXP_ONE_ILT(x) (((x) << 10) | x) +#define PXP_ILT_RANGE(f, l) (((l) << 10) | f) + +#define CNIC_ILT_LINES 0 + +static void bnx2x_ilt_wr(struct bnx2x *bp, u32 index, dma_addr_t addr) +{ + int reg; + + if (CHIP_IS_E1H(bp)) + reg = PXP2_REG_RQ_ONCHIP_AT_B0 + index*8; + else /* E1 */ + reg = PXP2_REG_RQ_ONCHIP_AT + index*8; + + bnx2x_wb_wr(bp, reg, ONCHIP_ADDR1(addr), ONCHIP_ADDR2(addr)); +} + +static int bnx2x_init_func(struct bnx2x *bp) +{ + int port = BP_PORT(bp); + int func = BP_FUNC(bp); + int i; + + DP(BNX2X_MSG_MCP, "starting func init func %x\n", func); + + i = FUNC_ILT_BASE(func); + + bnx2x_ilt_wr(bp, i, bnx2x_sp_mapping(bp, context)); + if (CHIP_IS_E1H(bp)) { + REG_WR(bp, PXP2_REG_RQ_CDU_FIRST_ILT, i); + REG_WR(bp, PXP2_REG_RQ_CDU_LAST_ILT, i + CNIC_ILT_LINES); + } else /* E1 */ + REG_WR(bp, PXP2_REG_PSWRQ_CDU0_L2P + func*4, + PXP_ILT_RANGE(i, i + CNIC_ILT_LINES)); + + + if (CHIP_IS_E1H(bp)) { + for (i = 0; i < 9; i++) + bnx2x_init_block(bp, + cm_start[func][i], cm_end[func][i]); + + REG_WR(bp, NIG_REG_LLH0_FUNC_EN + port*8, 1); + REG_WR(bp, NIG_REG_LLH0_FUNC_VLAN_ID + port*8, bp->e1hov); + } + + /* HC init per function */ + if (CHIP_IS_E1H(bp)) { + REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_12 + func*4, 0); + + REG_WR(bp, HC_REG_LEADING_EDGE_0 + port*8, 0); + REG_WR(bp, HC_REG_TRAILING_EDGE_0 + port*8, 0); + } + bnx2x_init_block(bp, hc_limits[func][0], hc_limits[func][1]); + + if (CHIP_IS_E1H(bp)) + REG_WR(bp, HC_REG_FUNC_NUM_P0 + port*4, func); + /* Reset PCIE errors for debug */ REG_WR(bp, 0x2114, 0xffffffff); REG_WR(bp, 0x2120, 0xffffffff); - REG_WR(bp, 0x2814, 0xffffffff); - /* !!! move to init_values.h */ - REG_WR(bp, XSDM_REG_INIT_CREDIT_PXP_CTRL, 0x1); - REG_WR(bp, USDM_REG_INIT_CREDIT_PXP_CTRL, 0x1); - REG_WR(bp, CSDM_REG_INIT_CREDIT_PXP_CTRL, 0x1); - REG_WR(bp, TSDM_REG_INIT_CREDIT_PXP_CTRL, 0x1); + return 0; +} + +static int bnx2x_init_hw(struct bnx2x *bp, u32 load_code) +{ + int i, rc = 0; - REG_WR(bp, DBG_REG_PCI_REQ_CREDIT, 0x1); - REG_WR(bp, TM_REG_PCIARB_CRDCNT_VAL, 0x1); - REG_WR(bp, CDU_REG_CDU_DEBUG, 0x264); - REG_WR(bp, CDU_REG_CDU_DEBUG, 0x0); + DP(BNX2X_MSG_MCP, "function %d load_code %x\n", + BP_FUNC(bp), load_code); - bnx2x_gunzip_end(bp); + bp->dmae_ready = 0; + mutex_init(&bp->dmae_mutex); + bnx2x_gunzip_init(bp); - if (!nomcp) { - port = bp->port; + switch (load_code) { + case FW_MSG_CODE_DRV_LOAD_COMMON: + rc = bnx2x_init_common(bp); + if (rc) + goto init_hw_err; + /* no break */ + + case FW_MSG_CODE_DRV_LOAD_PORT: + bp->dmae_ready = 1; + rc = bnx2x_init_port(bp); + if (rc) + goto init_hw_err; + /* no break */ + + case FW_MSG_CODE_DRV_LOAD_FUNCTION: + bp->dmae_ready = 1; + rc = bnx2x_init_func(bp); + if (rc) + goto init_hw_err; + break; + + default: + BNX2X_ERR("Unknown load_code (0x%x) from MCP\n", load_code); + break; + } + + if (!BP_NOMCP(bp)) { + int func = BP_FUNC(bp); bp->fw_drv_pulse_wr_seq = - (SHMEM_RD(bp, func_mb[port].drv_pulse_mb) & + (SHMEM_RD(bp, func_mb[func].drv_pulse_mb) & DRV_PULSE_SEQ_MASK); - bp->fw_mb = SHMEM_RD(bp, func_mb[port].fw_mb_param); - DP(BNX2X_MSG_MCP, "drv_pulse 0x%x fw_mb 0x%x\n", - bp->fw_drv_pulse_wr_seq, bp->fw_mb); - } else { - bp->fw_mb = 0; - } + bp->func_stx = SHMEM_RD(bp, func_mb[func].fw_mb_param); + DP(BNX2X_MSG_MCP, "drv_pulse 0x%x func_stx 0x%x\n", + bp->fw_drv_pulse_wr_seq, bp->func_stx); + } else + bp->func_stx = 0; - return 0; + /* this needs to be done before gunzip end */ + bnx2x_zero_def_sb(bp); + for_each_queue(bp, i) + bnx2x_zero_sb(bp, BP_L_ID(bp) + i); + +init_hw_err: + bnx2x_gunzip_end(bp); + + return rc; } /* send the MCP a request, block until there is a reply */ static u32 bnx2x_fw_command(struct bnx2x *bp, u32 command) { - int port = bp->port; + int func = BP_FUNC(bp); u32 seq = ++bp->fw_seq; u32 rc = 0; - SHMEM_WR(bp, func_mb[port].drv_mb_header, (command | seq)); + SHMEM_WR(bp, func_mb[func].drv_mb_header, (command | seq)); DP(BNX2X_MSG_MCP, "wrote command (%x) to FW MB\n", (command | seq)); /* let the FW do it's magic ... */ @@ -4164,7 +4712,7 @@ static u32 bnx2x_fw_command(struct bnx2x *bp, u32 command) if (CHIP_REV_IS_SLOW(bp)) msleep(900); - rc = SHMEM_RD(bp, func_mb[port].fw_mb_header); + rc = SHMEM_RD(bp, func_mb[func].fw_mb_header); DP(BNX2X_MSG_MCP, "read (%x) seq is (%x) from FW MB\n", rc, seq); /* is this a reply to our command? */ @@ -4229,15 +4777,13 @@ static void bnx2x_free_mem(struct bnx2x *bp) NUM_RCQ_BD); } - BNX2X_FREE(bp->fp); - /* end of fastpath */ BNX2X_PCI_FREE(bp->def_status_blk, bp->def_status_blk_mapping, - (sizeof(struct host_def_status_block))); + sizeof(struct host_def_status_block)); BNX2X_PCI_FREE(bp->slowpath, bp->slowpath_mapping, - (sizeof(struct bnx2x_slowpath))); + sizeof(struct bnx2x_slowpath)); #ifdef BCM_ISCSI BNX2X_PCI_FREE(bp->t1, bp->t1_mapping, 64*1024); @@ -4273,8 +4819,6 @@ static int bnx2x_alloc_mem(struct bnx2x *bp) int i; /* fastpath */ - BNX2X_ALLOC(bp->fp, sizeof(struct bnx2x_fastpath) * bp->num_queues); - for_each_queue(bp, i) { bnx2x_fp(bp, i, bp) = bp; @@ -4370,8 +4914,6 @@ static void bnx2x_free_tx_skbs(struct bnx2x *bp) u16 sw_prod = fp->tx_pkt_prod; u16 sw_cons = fp->tx_pkt_cons; - BUG_TRAP(fp->tx_buf_ring != NULL); - while (sw_cons != sw_prod) { bd_cons = bnx2x_free_tx_pkt(bp, fp, TX_BD(sw_cons)); sw_cons++; @@ -4386,8 +4928,6 @@ static void bnx2x_free_rx_skbs(struct bnx2x *bp) for_each_queue(bp, j) { struct bnx2x_fastpath *fp = &bp->fp[j]; - BUG_TRAP(fp->rx_buf_ring != NULL); - for (i = 0; i < NUM_RX_BD; i++) { struct sw_rx_bd *rx_buf = &fp->rx_buf_ring[i]; struct sk_buff *skb = rx_buf->skb; @@ -4414,7 +4954,7 @@ static void bnx2x_free_skbs(struct bnx2x *bp) static void bnx2x_free_msix_irqs(struct bnx2x *bp) { - int i; + int i, offset = 1; free_irq(bp->msix_table[0].vector, bp->dev); DP(NETIF_MSG_IFDOWN, "released sp irq (%d)\n", @@ -4422,26 +4962,22 @@ static void bnx2x_free_msix_irqs(struct bnx2x *bp) for_each_queue(bp, i) { DP(NETIF_MSG_IFDOWN, "about to release fp #%d->%d irq " - "state(%x)\n", i, bp->msix_table[i + 1].vector, + "state %x\n", i, bp->msix_table[i + offset].vector, bnx2x_fp(bp, i, state)); if (bnx2x_fp(bp, i, state) != BNX2X_FP_STATE_CLOSED) BNX2X_ERR("IRQ of fp #%d being freed while " "state != closed\n", i); - free_irq(bp->msix_table[i + 1].vector, &bp->fp[i]); + free_irq(bp->msix_table[i + offset].vector, &bp->fp[i]); } - } static void bnx2x_free_irq(struct bnx2x *bp) { - if (bp->flags & USING_MSIX_FLAG) { - bnx2x_free_msix_irqs(bp); pci_disable_msix(bp->pdev); - bp->flags &= ~USING_MSIX_FLAG; } else @@ -4450,87 +4986,87 @@ static void bnx2x_free_irq(struct bnx2x *bp) static int bnx2x_enable_msix(struct bnx2x *bp) { - - int i; + int i, rc, offset; bp->msix_table[0].entry = 0; - for_each_queue(bp, i) - bp->msix_table[i + 1].entry = i + 1; + offset = 1; + DP(NETIF_MSG_IFUP, "msix_table[0].entry = 0 (slowpath)\n"); - if (pci_enable_msix(bp->pdev, &bp->msix_table[0], - bp->num_queues + 1)){ - BNX2X_LOG("failed to enable MSI-X\n"); - return -1; + for_each_queue(bp, i) { + int igu_vec = offset + i + BP_L_ID(bp); + bp->msix_table[i + offset].entry = igu_vec; + DP(NETIF_MSG_IFUP, "msix_table[%d].entry = %d " + "(fastpath #%u)\n", i + offset, igu_vec, i); } + rc = pci_enable_msix(bp->pdev, &bp->msix_table[0], + bp->num_queues + offset); + if (rc) { + DP(NETIF_MSG_IFUP, "MSI-X is not attainable\n"); + return -1; + } bp->flags |= USING_MSIX_FLAG; return 0; - } - static int bnx2x_req_msix_irqs(struct bnx2x *bp) { - - int i, rc; + int i, rc, offset = 1; rc = request_irq(bp->msix_table[0].vector, bnx2x_msix_sp_int, 0, bp->dev->name, bp->dev); - if (rc) { BNX2X_ERR("request sp irq failed\n"); return -EBUSY; } for_each_queue(bp, i) { - rc = request_irq(bp->msix_table[i + 1].vector, + rc = request_irq(bp->msix_table[i + offset].vector, bnx2x_msix_fp_int, 0, bp->dev->name, &bp->fp[i]); - if (rc) { - BNX2X_ERR("request fp #%d irq failed " - "rc %d\n", i, rc); + BNX2X_ERR("request fp #%d irq failed rc %d\n", + i + offset, rc); bnx2x_free_msix_irqs(bp); return -EBUSY; } bnx2x_fp(bp, i, state) = BNX2X_FP_STATE_IRQ; - } return 0; - } static int bnx2x_req_irq(struct bnx2x *bp) { + int rc; - int rc = request_irq(bp->pdev->irq, bnx2x_interrupt, - IRQF_SHARED, bp->dev->name, bp->dev); + rc = request_irq(bp->pdev->irq, bnx2x_interrupt, IRQF_SHARED, + bp->dev->name, bp->dev); if (!rc) bnx2x_fp(bp, 0, state) = BNX2X_FP_STATE_IRQ; return rc; - } /* * Init service functions */ -static void bnx2x_set_mac_addr(struct bnx2x *bp) +static void bnx2x_set_mac_addr_e1(struct bnx2x *bp) { struct mac_configuration_cmd *config = bnx2x_sp(bp, mac_config); + int port = BP_PORT(bp); /* CAM allocation * unicasts 0-31:port0 32-63:port1 * multicast 64-127:port0 128-191:port1 */ config->hdr.length_6b = 2; - config->hdr.offset = bp->port ? 31 : 0; - config->hdr.reserved0 = 0; + config->hdr.offset = port ? 31 : 0; + config->hdr.client_id = BP_CL_ID(bp); config->hdr.reserved1 = 0; /* primary MAC */ @@ -4540,7 +5076,7 @@ static void bnx2x_set_mac_addr(struct bnx2x *bp) swab16(*(u16 *)&bp->dev->dev_addr[2]); config->config_table[0].cam_entry.lsb_mac_addr = swab16(*(u16 *)&bp->dev->dev_addr[4]); - config->config_table[0].cam_entry.flags = cpu_to_le16(bp->port); + config->config_table[0].cam_entry.flags = cpu_to_le16(port); config->config_table[0].target_table_entry.flags = 0; config->config_table[0].target_table_entry.client_id = 0; config->config_table[0].target_table_entry.vlan_id = 0; @@ -4554,7 +5090,7 @@ static void bnx2x_set_mac_addr(struct bnx2x *bp) config->config_table[1].cam_entry.msb_mac_addr = 0xffff; config->config_table[1].cam_entry.middle_mac_addr = 0xffff; config->config_table[1].cam_entry.lsb_mac_addr = 0xffff; - config->config_table[1].cam_entry.flags = cpu_to_le16(bp->port); + config->config_table[1].cam_entry.flags = cpu_to_le16(port); config->config_table[1].target_table_entry.flags = TSTORM_CAM_TARGET_TABLE_ENTRY_BROADCAST; config->config_table[1].target_table_entry.client_id = 0; @@ -4565,64 +5101,105 @@ static void bnx2x_set_mac_addr(struct bnx2x *bp) U64_LO(bnx2x_sp_mapping(bp, mac_config)), 0); } +static void bnx2x_set_mac_addr_e1h(struct bnx2x *bp) +{ + struct mac_configuration_cmd_e1h *config = + (struct mac_configuration_cmd_e1h *)bnx2x_sp(bp, mac_config); + + if (bp->state != BNX2X_STATE_OPEN) { + DP(NETIF_MSG_IFUP, "state is %x, returning\n", bp->state); + return; + } + + /* CAM allocation for E1H + * unicasts: by func number + * multicast: 20+FUNC*20, 20 each + */ + config->hdr.length_6b = 1; + config->hdr.offset = BP_FUNC(bp); + config->hdr.client_id = BP_CL_ID(bp); + config->hdr.reserved1 = 0; + + /* primary MAC */ + config->config_table[0].msb_mac_addr = + swab16(*(u16 *)&bp->dev->dev_addr[0]); + config->config_table[0].middle_mac_addr = + swab16(*(u16 *)&bp->dev->dev_addr[2]); + config->config_table[0].lsb_mac_addr = + swab16(*(u16 *)&bp->dev->dev_addr[4]); + config->config_table[0].client_id = BP_L_ID(bp); + config->config_table[0].vlan_id = 0; + config->config_table[0].e1hov_id = cpu_to_le16(bp->e1hov); + config->config_table[0].flags = BP_PORT(bp); + + DP(NETIF_MSG_IFUP, "setting MAC (%04x:%04x:%04x) E1HOV %d CLID %d\n", + config->config_table[0].msb_mac_addr, + config->config_table[0].middle_mac_addr, + config->config_table[0].lsb_mac_addr, bp->e1hov, BP_L_ID(bp)); + + bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_SET_MAC, 0, + U64_HI(bnx2x_sp_mapping(bp, mac_config)), + U64_LO(bnx2x_sp_mapping(bp, mac_config)), 0); +} + static int bnx2x_wait_ramrod(struct bnx2x *bp, int state, int idx, int *state_p, int poll) { /* can take a while if any port is running */ - int timeout = 500; + int cnt = 500; DP(NETIF_MSG_IFUP, "%s for state to become %x on IDX [%d]\n", poll ? "polling" : "waiting", state, idx); might_sleep(); - - while (timeout) { - + while (cnt--) { if (poll) { bnx2x_rx_int(bp->fp, 10); - /* If index is different from 0 - * The reply for some commands will + /* if index is different from 0 + * the reply for some commands will * be on the none default queue */ if (idx) bnx2x_rx_int(&bp->fp[idx], 10); } - - mb(); /* state is changed by bnx2x_sp_event()*/ + mb(); /* state is changed by bnx2x_sp_event() */ if (*state_p == state) return 0; - timeout--; msleep(1); - } /* timeout! */ BNX2X_ERR("timeout %s for state %x on IDX [%d]\n", poll ? "polling" : "waiting", state, idx); +#ifdef BNX2X_STOP_ON_ERROR + bnx2x_panic(); +#endif return -EBUSY; } static int bnx2x_setup_leading(struct bnx2x *bp) { + int rc; /* reset IGU state */ - bnx2x_ack_sb(bp, DEF_SB_ID, CSTORM_ID, 0, IGU_INT_ENABLE, 0); + bnx2x_ack_sb(bp, bp->fp[0].sb_id, CSTORM_ID, 0, IGU_INT_ENABLE, 0); /* SETUP ramrod */ bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_PORT_SETUP, 0, 0, 0, 0); - return bnx2x_wait_ramrod(bp, BNX2X_STATE_OPEN, 0, &(bp->state), 0); + /* Wait for completion */ + rc = bnx2x_wait_ramrod(bp, BNX2X_STATE_OPEN, 0, &(bp->state), 0); + return rc; } static int bnx2x_setup_multi(struct bnx2x *bp, int index) { - /* reset IGU state */ - bnx2x_ack_sb(bp, index, CSTORM_ID, 0, IGU_INT_ENABLE, 0); + bnx2x_ack_sb(bp, bp->fp[index].sb_id, CSTORM_ID, 0, IGU_INT_ENABLE, 0); /* SETUP ramrod */ bp->fp[index].state = BNX2X_FP_STATE_OPENING; @@ -4631,82 +5208,107 @@ static int bnx2x_setup_multi(struct bnx2x *bp, int index) /* Wait for completion */ return bnx2x_wait_ramrod(bp, BNX2X_FP_STATE_OPEN, index, &(bp->fp[index].state), 0); - } - static int bnx2x_poll(struct napi_struct *napi, int budget); static void bnx2x_set_rx_mode(struct net_device *dev); -static int bnx2x_nic_load(struct bnx2x *bp, int req_irq) +/* must be called with rtnl_lock */ +static int bnx2x_nic_load(struct bnx2x *bp, int load_mode) { u32 load_code; - int i; + int i, rc; + +#ifdef BNX2X_STOP_ON_ERROR + if (unlikely(bp->panic)) + return -EPERM; +#endif bp->state = BNX2X_STATE_OPENING_WAIT4_LOAD; - /* Send LOAD_REQUEST command to MCP. - Returns the type of LOAD command: if it is the - first port to be initialized common blocks should be - initialized, otherwise - not. + /* Send LOAD_REQUEST command to MCP + Returns the type of LOAD command: + if it is the first port to be initialized + common blocks should be initialized, otherwise - not */ - if (!nomcp) { + if (!BP_NOMCP(bp)) { load_code = bnx2x_fw_command(bp, DRV_MSG_CODE_LOAD_REQ); if (!load_code) { BNX2X_ERR("MCP response failure, unloading\n"); return -EBUSY; } - if (load_code == FW_MSG_CODE_DRV_LOAD_REFUSED) { - BNX2X_ERR("MCP refused load request, unloading\n"); + if (load_code == FW_MSG_CODE_DRV_LOAD_REFUSED) return -EBUSY; /* other port in diagnostic mode */ - } + } else { - load_code = FW_MSG_CODE_DRV_LOAD_COMMON; + DP(NETIF_MSG_IFUP, "NO MCP load counts before us %d, %d, %d\n", + load_count[0], load_count[1], load_count[2]); + load_count[0]++; + load_count[1 + BP_PORT(bp)]++; + DP(NETIF_MSG_IFUP, "NO MCP new load counts %d, %d, %d\n", + load_count[0], load_count[1], load_count[2]); + if (load_count[0] == 1) + load_code = FW_MSG_CODE_DRV_LOAD_COMMON; + else if (load_count[1 + BP_PORT(bp)] == 1) + load_code = FW_MSG_CODE_DRV_LOAD_PORT; + else + load_code = FW_MSG_CODE_DRV_LOAD_FUNCTION; } - /* if we can't use msix we only need one fp, - * so try to enable msix with the requested number of fp's + if ((load_code == FW_MSG_CODE_DRV_LOAD_COMMON) || + (load_code == FW_MSG_CODE_DRV_LOAD_PORT)) + bp->port.pmf = 1; + else + bp->port.pmf = 0; + DP(NETIF_MSG_LINK, "pmf %d\n", bp->port.pmf); + + /* if we can't use MSI-X we only need one fp, + * so try to enable MSI-X with the requested number of fp's * and fallback to inta with one fp */ - if (req_irq) { - if (use_inta) { + if (use_inta) { + bp->num_queues = 1; + + } else { + if ((use_multi > 1) && (use_multi <= BP_MAX_QUEUES(bp))) + /* user requested number */ + bp->num_queues = use_multi; + + else if (use_multi) + bp->num_queues = min_t(u32, num_online_cpus(), + BP_MAX_QUEUES(bp)); + else bp->num_queues = 1; - } else { - if ((use_multi > 1) && (use_multi <= 16)) - /* user requested number */ - bp->num_queues = use_multi; - else if (use_multi == 1) - bp->num_queues = num_online_cpus(); - else - bp->num_queues = 1; - - if (bnx2x_enable_msix(bp)) { - /* failed to enable msix */ - bp->num_queues = 1; - if (use_multi) - BNX2X_ERR("Multi requested but failed" - " to enable MSI-X\n"); - } + + if (bnx2x_enable_msix(bp)) { + /* failed to enable MSI-X */ + bp->num_queues = 1; + if (use_multi) + BNX2X_ERR("Multi requested but failed" + " to enable MSI-X\n"); } } - - DP(NETIF_MSG_IFUP, "set number of queues to %d\n", bp->num_queues); + DP(NETIF_MSG_IFUP, + "set number of queues to %d\n", bp->num_queues); if (bnx2x_alloc_mem(bp)) return -ENOMEM; - if (req_irq) { - if (bp->flags & USING_MSIX_FLAG) { - if (bnx2x_req_msix_irqs(bp)) { - pci_disable_msix(bp->pdev); - goto load_error; - } + /* Disable interrupt handling until HW is initialized */ + atomic_set(&bp->intr_sem, 1); - } else { - if (bnx2x_req_irq(bp)) { - BNX2X_ERR("IRQ request failed, aborting\n"); - goto load_error; - } + if (bp->flags & USING_MSIX_FLAG) { + rc = bnx2x_req_msix_irqs(bp); + if (rc) { + pci_disable_msix(bp->pdev); + goto load_error; + } + } else { + bnx2x_ack_int(bp); + rc = bnx2x_req_irq(bp); + if (rc) { + BNX2X_ERR("IRQ request failed, aborting\n"); + goto load_error; } } @@ -4714,26 +5316,25 @@ static int bnx2x_nic_load(struct bnx2x *bp, int req_irq) netif_napi_add(bp->dev, &bnx2x_fp(bp, i, napi), bnx2x_poll, 128); - /* Initialize HW */ - if (bnx2x_function_init(bp, - (load_code == FW_MSG_CODE_DRV_LOAD_COMMON))) { + rc = bnx2x_init_hw(bp, load_code); + if (rc) { BNX2X_ERR("HW init failed, aborting\n"); goto load_error; } - + /* Enable interrupt handling */ atomic_set(&bp->intr_sem, 0); - /* Setup NIC internals and enable interrupts */ bnx2x_nic_init(bp); /* Send LOAD_DONE command to MCP */ - if (!nomcp) { + if (!BP_NOMCP(bp)) { load_code = bnx2x_fw_command(bp, DRV_MSG_CODE_LOAD_DONE); if (!load_code) { BNX2X_ERR("MCP response failure, unloading\n"); + rc = -EBUSY; goto load_int_disable; } } @@ -4745,33 +5346,68 @@ static int bnx2x_nic_load(struct bnx2x *bp, int req_irq) for_each_queue(bp, i) napi_enable(&bnx2x_fp(bp, i, napi)); - if (bnx2x_setup_leading(bp)) + rc = bnx2x_setup_leading(bp); + if (rc) { +#ifdef BNX2X_STOP_ON_ERROR + bp->panic = 1; +#endif goto load_stop_netif; + } - for_each_nondefault_queue(bp, i) - if (bnx2x_setup_multi(bp, i)) - goto load_stop_netif; + if (CHIP_IS_E1H(bp)) + if (bp->mf_config & FUNC_MF_CFG_FUNC_DISABLED) { + BNX2X_ERR("!!! mf_cfg function disabled\n"); + bp->state = BNX2X_STATE_DISABLED; + } - bnx2x_set_mac_addr(bp); + if (bp->state == BNX2X_STATE_OPEN) + for_each_nondefault_queue(bp, i) { + rc = bnx2x_setup_multi(bp, i); + if (rc) + goto load_stop_netif; + } - bnx2x_initial_phy_init(bp); + if (CHIP_IS_E1(bp)) + bnx2x_set_mac_addr_e1(bp); + else + bnx2x_set_mac_addr_e1h(bp); + + if (bp->port.pmf) + bnx2x_initial_phy_init(bp); /* Start fast path */ - if (req_irq) { /* IRQ is only requested from bnx2x_open */ + switch (load_mode) { + case LOAD_NORMAL: + /* Tx queue should be only reenabled */ + netif_wake_queue(bp->dev); + bnx2x_set_rx_mode(bp->dev); + break; + + case LOAD_OPEN: + /* IRQ is only requested from bnx2x_open */ netif_start_queue(bp->dev); + bnx2x_set_rx_mode(bp->dev); if (bp->flags & USING_MSIX_FLAG) printk(KERN_INFO PFX "%s: using MSI-X\n", bp->dev->name); + break; - /* Otherwise Tx queue should be only reenabled */ - } else if (netif_running(bp->dev)) { - netif_wake_queue(bp->dev); + case LOAD_DIAG: bnx2x_set_rx_mode(bp->dev); + bp->state = BNX2X_STATE_DIAG; + break; + + default: + break; } + if (!bp->port.pmf) + bnx2x__link_status_update(bp); + /* start the timer */ mod_timer(&bp->timer, jiffies + bp->current_interval); + return 0; load_stop_netif: @@ -4781,7 +5417,7 @@ load_stop_netif: load_int_disable: bnx2x_int_disable_sync(bp); - bnx2x_free_skbs(bp); + /* Release IRQs */ bnx2x_free_irq(bp); load_error: @@ -4789,95 +5425,50 @@ load_error: /* TBD we really need to reset the chip if we want to recover from this */ - return -EBUSY; -} - - -static void bnx2x_reset_chip(struct bnx2x *bp, u32 reset_code) -{ - int port = bp->port; -#ifdef USE_DMAE - u32 wb_write[2]; -#endif - int base, i; - - DP(NETIF_MSG_IFDOWN, "reset called with code %x\n", reset_code); - - /* Do not rcv packets to BRB */ - REG_WR(bp, NIG_REG_LLH0_BRB1_DRV_MASK + port*4, 0x0); - /* Do not direct rcv packets that are not for MCP to the BRB */ - REG_WR(bp, (port ? NIG_REG_LLH1_BRB1_NOT_MCP : - NIG_REG_LLH0_BRB1_NOT_MCP), 0x0); - - /* Configure IGU and AEU */ - REG_WR(bp, HC_REG_CONFIG_0 + port*4, 0x1000); - REG_WR(bp, MISC_REG_AEU_MASK_ATTN_FUNC_0 + port*4, 0); - - /* TODO: Close Doorbell port? */ - - /* Clear ILT */ -#ifdef USE_DMAE - wb_write[0] = 0; - wb_write[1] = 0; -#endif - base = port * RQ_ONCHIP_AT_PORT_SIZE; - for (i = base; i < base + RQ_ONCHIP_AT_PORT_SIZE; i++) { -#ifdef USE_DMAE - REG_WR_DMAE(bp, PXP2_REG_RQ_ONCHIP_AT + i*8, wb_write, 2); -#else - REG_WR_IND(bp, PXP2_REG_RQ_ONCHIP_AT, 0); - REG_WR_IND(bp, PXP2_REG_RQ_ONCHIP_AT + 4, 0); -#endif - } - - if (reset_code == FW_MSG_CODE_DRV_UNLOAD_COMMON) { - /* reset_common */ - REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR, - 0xd3ffff7f); - REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, - 0x1403); - } + return rc; } static int bnx2x_stop_multi(struct bnx2x *bp, int index) { - int rc; /* halt the connection */ bp->fp[index].state = BNX2X_FP_STATE_HALTING; bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_HALT, index, 0, 0, 0); - + /* Wait for completion */ rc = bnx2x_wait_ramrod(bp, BNX2X_FP_STATE_HALTED, index, - &(bp->fp[index].state), 1); + &(bp->fp[index].state), 1); if (rc) /* timeout */ return rc; /* delete cfc entry */ bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_CFC_DEL, index, 0, 0, 1); - return bnx2x_wait_ramrod(bp, BNX2X_FP_STATE_CLOSED, index, - &(bp->fp[index].state), 1); - + /* Wait for completion */ + rc = bnx2x_wait_ramrod(bp, BNX2X_FP_STATE_CLOSED, index, + &(bp->fp[index].state), 1); + return rc; } - static void bnx2x_stop_leading(struct bnx2x *bp) { u16 dsb_sp_prod_idx; /* if the other port is handling traffic, this can take a lot of time */ - int timeout = 500; + int cnt = 500; + int rc; might_sleep(); /* Send HALT ramrod */ bp->fp[0].state = BNX2X_FP_STATE_HALTING; - bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_HALT, 0, 0, 0, 0); + bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_HALT, 0, 0, BP_CL_ID(bp), 0); - if (bnx2x_wait_ramrod(bp, BNX2X_FP_STATE_HALTED, 0, - &(bp->fp[0].state), 1)) + /* Wait for completion */ + rc = bnx2x_wait_ramrod(bp, BNX2X_FP_STATE_HALTED, 0, + &(bp->fp[0].state), 1); + if (rc) /* timeout */ return; dsb_sp_prod_idx = *bp->dsb_sp_prod; @@ -4889,29 +5480,110 @@ static void bnx2x_stop_leading(struct bnx2x *bp) we are going to reset the chip anyway so there is not much to do if this times out */ - while ((dsb_sp_prod_idx == *bp->dsb_sp_prod) && timeout) { - timeout--; + while (dsb_sp_prod_idx == *bp->dsb_sp_prod) { msleep(1); - } - if (!timeout) { - DP(NETIF_MSG_IFDOWN, "timeout polling for completion " - "dsb_sp_prod 0x%x != dsb_sp_prod_idx 0x%x\n", - *bp->dsb_sp_prod, dsb_sp_prod_idx); + if (!cnt) { + DP(NETIF_MSG_IFDOWN, "timeout waiting for port del " + "dsb_sp_prod 0x%x != dsb_sp_prod_idx 0x%x\n", + *bp->dsb_sp_prod, dsb_sp_prod_idx); +#ifdef BNX2X_STOP_ON_ERROR + bnx2x_panic(); +#endif + break; + } + cnt--; } bp->state = BNX2X_STATE_CLOSING_WAIT4_UNLOAD; bp->fp[0].state = BNX2X_FP_STATE_CLOSED; } +static void bnx2x_reset_func(struct bnx2x *bp) +{ + int port = BP_PORT(bp); + int func = BP_FUNC(bp); + int base, i; + + /* Configure IGU */ + REG_WR(bp, HC_REG_LEADING_EDGE_0 + port*8, 0); + REG_WR(bp, HC_REG_TRAILING_EDGE_0 + port*8, 0); + + REG_WR(bp, HC_REG_CONFIG_0 + port*4, 0x1000); + + /* Clear ILT */ + base = FUNC_ILT_BASE(func); + for (i = base; i < base + ILT_PER_FUNC; i++) + bnx2x_ilt_wr(bp, i, 0); +} + +static void bnx2x_reset_port(struct bnx2x *bp) +{ + int port = BP_PORT(bp); + u32 val; + + REG_WR(bp, NIG_REG_MASK_INTERRUPT_PORT0 + port*4, 0); + + /* Do not rcv packets to BRB */ + REG_WR(bp, NIG_REG_LLH0_BRB1_DRV_MASK + port*4, 0x0); + /* Do not direct rcv packets that are not for MCP to the BRB */ + REG_WR(bp, (port ? NIG_REG_LLH1_BRB1_NOT_MCP : + NIG_REG_LLH0_BRB1_NOT_MCP), 0x0); + + /* Configure AEU */ + REG_WR(bp, MISC_REG_AEU_MASK_ATTN_FUNC_0 + port*4, 0); + + msleep(100); + /* Check for BRB port occupancy */ + val = REG_RD(bp, BRB1_REG_PORT_NUM_OCC_BLOCKS_0 + port*4); + if (val) + DP(NETIF_MSG_IFDOWN, + "BRB1 is not empty %d blooks are occupied\n", val); + + /* TODO: Close Doorbell port? */ +} + +static void bnx2x_reset_common(struct bnx2x *bp) +{ + /* reset_common */ + REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR, + 0xd3ffff7f); + REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, 0x1403); +} + +static void bnx2x_reset_chip(struct bnx2x *bp, u32 reset_code) +{ + DP(BNX2X_MSG_MCP, "function %d reset_code %x\n", + BP_FUNC(bp), reset_code); + + switch (reset_code) { + case FW_MSG_CODE_DRV_UNLOAD_COMMON: + bnx2x_reset_port(bp); + bnx2x_reset_func(bp); + bnx2x_reset_common(bp); + break; + + case FW_MSG_CODE_DRV_UNLOAD_PORT: + bnx2x_reset_port(bp); + bnx2x_reset_func(bp); + break; + + case FW_MSG_CODE_DRV_UNLOAD_FUNCTION: + bnx2x_reset_func(bp); + break; -static int bnx2x_nic_unload(struct bnx2x *bp, int free_irq) + default: + BNX2X_ERR("Unknown reset_code (0x%x) from MCP\n", reset_code); + break; + } +} + +/* msut be called with rtnl_lock */ +static int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode) { u32 reset_code = 0; - int i, timeout; + int i, cnt; bp->state = BNX2X_STATE_CLOSING_WAIT4_HALT; - del_timer_sync(&bp->timer); - bp->rx_mode = BNX2X_RX_MODE_NONE; bnx2x_set_storm_rx_mode(bp); @@ -4920,21 +5592,44 @@ static int bnx2x_nic_unload(struct bnx2x *bp, int free_irq) bp->dev->trans_start = jiffies; /* prevent tx timeout */ } + del_timer_sync(&bp->timer); + SHMEM_WR(bp, func_mb[BP_FUNC(bp)].drv_pulse_mb, + (DRV_PULSE_ALWAYS_ALIVE | bp->fw_drv_pulse_wr_seq)); + /* Wait until all fast path tasks complete */ for_each_queue(bp, i) { struct bnx2x_fastpath *fp = &bp->fp[i]; - timeout = 1000; - while (bnx2x_has_work(fp) && (timeout--)) +#ifdef BNX2X_STOP_ON_ERROR +#ifdef __powerpc64__ + DP(NETIF_MSG_RX_STATUS, "fp->tpa_queue_used = 0x%lx\n", +#else + DP(NETIF_MSG_IFDOWN, "fp->tpa_queue_used = 0x%llx\n", +#endif + fp->tpa_queue_used); +#endif + cnt = 1000; + smp_rmb(); + while (bnx2x_has_work(fp)) { msleep(1); - if (!timeout) - BNX2X_ERR("timeout waiting for queue[%d]\n", i); + if (!cnt) { + BNX2X_ERR("timeout waiting for queue[%d]\n", + i); +#ifdef BNX2X_STOP_ON_ERROR + bnx2x_panic(); + return -EBUSY; +#else + break; +#endif + } + cnt--; + smp_rmb(); + } } - /* Wait until stat ramrod returns and all SP tasks complete */ - timeout = 1000; - while ((bp->stat_pending || (bp->spq_left != MAX_SPQ_PENDING)) && - (timeout--)) + /* Wait until all slow path tasks complete */ + cnt = 1000; + while ((bp->spq_left != MAX_SPQ_PENDING) && cnt--) msleep(1); for_each_queue(bp, i) @@ -4942,59 +5637,84 @@ static int bnx2x_nic_unload(struct bnx2x *bp, int free_irq) /* Disable interrupts after Tx and Rx are disabled on stack level */ bnx2x_int_disable_sync(bp); + /* Release IRQs */ + bnx2x_free_irq(bp); + if (bp->flags & NO_WOL_FLAG) reset_code = DRV_MSG_CODE_UNLOAD_REQ_WOL_MCP; else if (bp->wol) { - u32 emac_base = bp->port ? GRCBASE_EMAC0 : GRCBASE_EMAC1; + u32 emac_base = BP_PORT(bp) ? GRCBASE_EMAC1 : GRCBASE_EMAC0; u8 *mac_addr = bp->dev->dev_addr; - u32 val = (EMAC_MODE_MPKT | EMAC_MODE_MPKT_RCVD | - EMAC_MODE_ACPI_RCVD); - - EMAC_WR(EMAC_REG_EMAC_MODE, val); + u32 val; + /* The mac address is written to entries 1-4 to + preserve entry 0 which is used by the PMF */ val = (mac_addr[0] << 8) | mac_addr[1]; - EMAC_WR(EMAC_REG_EMAC_MAC_MATCH, val); + EMAC_WR(EMAC_REG_EMAC_MAC_MATCH + (BP_E1HVN(bp) + 1)*8, val); val = (mac_addr[2] << 24) | (mac_addr[3] << 16) | (mac_addr[4] << 8) | mac_addr[5]; - EMAC_WR(EMAC_REG_EMAC_MAC_MATCH + 4, val); + EMAC_WR(EMAC_REG_EMAC_MAC_MATCH + (BP_E1HVN(bp) + 1)*8 + 4, + val); reset_code = DRV_MSG_CODE_UNLOAD_REQ_WOL_EN; } else reset_code = DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS; - /* Close multi and leading connections */ + /* Close multi and leading connections + Completions for ramrods are collected in a synchronous way */ for_each_nondefault_queue(bp, i) if (bnx2x_stop_multi(bp, i)) goto unload_error; - bnx2x_stop_leading(bp); + if (CHIP_IS_E1H(bp)) + REG_WR(bp, NIG_REG_LLH0_FUNC_EN + BP_PORT(bp)*8, 0); + + bnx2x_stop_leading(bp); +#ifdef BNX2X_STOP_ON_ERROR + /* If ramrod completion timed out - break here! */ + if (bp->panic) { + BNX2X_ERR("Stop leading failed!\n"); + return -EBUSY; + } +#endif + if ((bp->state != BNX2X_STATE_CLOSING_WAIT4_UNLOAD) || (bp->fp[0].state != BNX2X_FP_STATE_CLOSED)) { - DP(NETIF_MSG_IFDOWN, "failed to close leading properly!" - "state 0x%x fp[0].state 0x%x", + DP(NETIF_MSG_IFDOWN, "failed to close leading properly! " + "state 0x%x fp[0].state 0x%x\n", bp->state, bp->fp[0].state); } unload_error: - bnx2x__link_reset(bp); - - if (!nomcp) + if (!BP_NOMCP(bp)) reset_code = bnx2x_fw_command(bp, reset_code); - else - reset_code = FW_MSG_CODE_DRV_UNLOAD_COMMON; + else { + DP(NETIF_MSG_IFDOWN, "NO MCP load counts %d, %d, %d\n", + load_count[0], load_count[1], load_count[2]); + load_count[0]--; + load_count[1 + BP_PORT(bp)]--; + DP(NETIF_MSG_IFDOWN, "NO MCP new load counts %d, %d, %d\n", + load_count[0], load_count[1], load_count[2]); + if (load_count[0] == 0) + reset_code = FW_MSG_CODE_DRV_UNLOAD_COMMON; + else if (load_count[1 + BP_PORT(bp)] == 0) + reset_code = FW_MSG_CODE_DRV_UNLOAD_PORT; + else + reset_code = FW_MSG_CODE_DRV_UNLOAD_FUNCTION; + } - /* Release IRQs */ - if (free_irq) - bnx2x_free_irq(bp); + if ((reset_code == FW_MSG_CODE_DRV_UNLOAD_COMMON) || + (reset_code == FW_MSG_CODE_DRV_UNLOAD_PORT)) + bnx2x__link_reset(bp); /* Reset the chip */ bnx2x_reset_chip(bp, reset_code); /* Report UNLOAD_DONE to MCP */ - if (!nomcp) + if (!BP_NOMCP(bp)) bnx2x_fw_command(bp, DRV_MSG_CODE_UNLOAD_DONE); /* Free SKBs and driver internals */ @@ -5008,6 +5728,29 @@ unload_error: return 0; } +static void bnx2x_reset_task(struct work_struct *work) +{ + struct bnx2x *bp = container_of(work, struct bnx2x, reset_task); + +#ifdef BNX2X_STOP_ON_ERROR + BNX2X_ERR("reset task called but STOP_ON_ERROR defined" + " so reset not done to allow debug dump,\n" + KERN_ERR " you will need to reboot when done\n"); + return; +#endif + + rtnl_lock(); + + if (!netif_running(bp->dev)) + goto reset_task_exit; + + bnx2x_nic_unload(bp, UNLOAD_NORMAL); + bnx2x_nic_load(bp, LOAD_NORMAL); + +reset_task_exit: + rtnl_unlock(); +} + /* end of nic load/unload */ /* ethtool_ops */ @@ -5016,9 +5759,139 @@ unload_error: * Init service functions */ -static void bnx2x_link_settings_supported(struct bnx2x *bp, u32 switch_cfg) +static void __devinit bnx2x_undi_unload(struct bnx2x *bp) +{ + u32 val; + + /* Check if there is any driver already loaded */ + val = REG_RD(bp, MISC_REG_UNPREPARED); + if (val == 0x1) { + /* Check if it is the UNDI driver + * UNDI driver initializes CID offset for normal bell to 0x7 + */ + val = REG_RD(bp, DORQ_REG_NORM_CID_OFST); + if (val == 0x7) { + u32 reset_code = DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS; + /* save our func and fw_seq */ + int func = BP_FUNC(bp); + u16 fw_seq = bp->fw_seq; + + BNX2X_DEV_INFO("UNDI is active! reset device\n"); + + /* try unload UNDI on port 0 */ + bp->func = 0; + bp->fw_seq = (SHMEM_RD(bp, + func_mb[bp->func].drv_mb_header) & + DRV_MSG_SEQ_NUMBER_MASK); + + reset_code = bnx2x_fw_command(bp, reset_code); + bnx2x_fw_command(bp, DRV_MSG_CODE_UNLOAD_DONE); + + /* if UNDI is loaded on the other port */ + if (reset_code != FW_MSG_CODE_DRV_UNLOAD_COMMON) { + + bp->func = 1; + bp->fw_seq = (SHMEM_RD(bp, + func_mb[bp->func].drv_mb_header) & + DRV_MSG_SEQ_NUMBER_MASK); + + bnx2x_fw_command(bp, + DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS); + bnx2x_fw_command(bp, + DRV_MSG_CODE_UNLOAD_DONE); + + /* restore our func and fw_seq */ + bp->func = func; + bp->fw_seq = fw_seq; + } + + /* reset device */ + REG_WR(bp, + GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR, + 0xd3ffff7f); + REG_WR(bp, + GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, + 0x1403); + } + } +} + +static void __devinit bnx2x_get_common_hwinfo(struct bnx2x *bp) +{ + u32 val, val2, val3, val4, id; + + /* Get the chip revision id and number. */ + /* chip num:16-31, rev:12-15, metal:4-11, bond_id:0-3 */ + val = REG_RD(bp, MISC_REG_CHIP_NUM); + id = ((val & 0xffff) << 16); + val = REG_RD(bp, MISC_REG_CHIP_REV); + id |= ((val & 0xf) << 12); + val = REG_RD(bp, MISC_REG_CHIP_METAL); + id |= ((val & 0xff) << 4); + REG_RD(bp, MISC_REG_BOND_ID); + id |= (val & 0xf); + bp->common.chip_id = id; + bp->link_params.chip_id = bp->common.chip_id; + BNX2X_DEV_INFO("chip ID is 0x%x\n", id); + + val = REG_RD(bp, MCP_REG_MCPR_NVM_CFG4); + bp->common.flash_size = (NVRAM_1MB_SIZE << + (val & MCPR_NVM_CFG4_FLASH_SIZE)); + BNX2X_DEV_INFO("flash_size 0x%x (%d)\n", + bp->common.flash_size, bp->common.flash_size); + + bp->common.shmem_base = REG_RD(bp, MISC_REG_SHARED_MEM_ADDR); + bp->link_params.shmem_base = bp->common.shmem_base; + BNX2X_DEV_INFO("shmem offset is 0x%x\n", bp->common.shmem_base); + + if (!bp->common.shmem_base || + (bp->common.shmem_base < 0xA0000) || + (bp->common.shmem_base >= 0xC0000)) { + BNX2X_DEV_INFO("MCP not active\n"); + bp->flags |= NO_MCP_FLAG; + return; + } + + val = SHMEM_RD(bp, validity_map[BP_PORT(bp)]); + if ((val & (SHR_MEM_VALIDITY_DEV_INFO | SHR_MEM_VALIDITY_MB)) + != (SHR_MEM_VALIDITY_DEV_INFO | SHR_MEM_VALIDITY_MB)) + BNX2X_ERR("BAD MCP validity signature\n"); + + bp->common.hw_config = SHMEM_RD(bp, dev_info.shared_hw_config.config); + bp->common.board = SHMEM_RD(bp, dev_info.shared_hw_config.board); + + BNX2X_DEV_INFO("hw_config 0x%08x board 0x%08x\n", + bp->common.hw_config, bp->common.board); + + bp->link_params.hw_led_mode = ((bp->common.hw_config & + SHARED_HW_CFG_LED_MODE_MASK) >> + SHARED_HW_CFG_LED_MODE_SHIFT); + + val = SHMEM_RD(bp, dev_info.bc_rev) >> 8; + bp->common.bc_ver = val; + BNX2X_DEV_INFO("bc_ver %X\n", val); + if (val < BNX2X_BC_VER) { + /* for now only warn + * later we might need to enforce this */ + BNX2X_ERR("This driver needs bc_ver %X but found %X," + " please upgrade BC\n", BNX2X_BC_VER, val); + } + BNX2X_DEV_INFO("%sWoL Capable\n", + (bp->flags & NO_WOL_FLAG)? "Not " : ""); + + val = SHMEM_RD(bp, dev_info.shared_hw_config.part_num); + val2 = SHMEM_RD(bp, dev_info.shared_hw_config.part_num[4]); + val3 = SHMEM_RD(bp, dev_info.shared_hw_config.part_num[8]); + val4 = SHMEM_RD(bp, dev_info.shared_hw_config.part_num[12]); + + printk(KERN_INFO PFX "part number %X-%X-%X-%X\n", + val, val2, val3, val4); +} + +static void __devinit bnx2x_link_settings_supported(struct bnx2x *bp, + u32 switch_cfg) { - int port = bp->port; + int port = BP_PORT(bp); u32 ext_phy_type; switch (switch_cfg) { @@ -5032,31 +5905,33 @@ static void bnx2x_link_settings_supported(struct bnx2x *bp, u32 switch_cfg) BNX2X_DEV_INFO("ext_phy_type 0x%x (Direct)\n", ext_phy_type); - bp->supported |= (SUPPORTED_10baseT_Half | - SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | - SUPPORTED_100baseT_Full | - SUPPORTED_1000baseT_Full | - SUPPORTED_2500baseX_Full | - SUPPORTED_TP | SUPPORTED_FIBRE | - SUPPORTED_Autoneg | - SUPPORTED_Pause | - SUPPORTED_Asym_Pause); + bp->port.supported |= (SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Full | + SUPPORTED_2500baseX_Full | + SUPPORTED_TP | + SUPPORTED_FIBRE | + SUPPORTED_Autoneg | + SUPPORTED_Pause | + SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_BCM5482: BNX2X_DEV_INFO("ext_phy_type 0x%x (5482)\n", ext_phy_type); - bp->supported |= (SUPPORTED_10baseT_Half | - SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | - SUPPORTED_100baseT_Full | - SUPPORTED_1000baseT_Full | - SUPPORTED_TP | SUPPORTED_FIBRE | - SUPPORTED_Autoneg | - SUPPORTED_Pause | - SUPPORTED_Asym_Pause); + bp->port.supported |= (SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Full | + SUPPORTED_TP | + SUPPORTED_FIBRE | + SUPPORTED_Autoneg | + SUPPORTED_Pause | + SUPPORTED_Asym_Pause); break; default: @@ -5066,9 +5941,9 @@ static void bnx2x_link_settings_supported(struct bnx2x *bp, u32 switch_cfg) return; } - bp->phy_addr = REG_RD(bp, NIG_REG_SERDES0_CTRL_PHY_ADDR + - port*0x10); - BNX2X_DEV_INFO("phy_addr 0x%x\n", bp->phy_addr); + bp->port.phy_addr = REG_RD(bp, NIG_REG_SERDES0_CTRL_PHY_ADDR + + port*0x10); + BNX2X_DEV_INFO("phy_addr 0x%x\n", bp->port.phy_addr); break; case SWITCH_CFG_10G: @@ -5081,75 +5956,75 @@ static void bnx2x_link_settings_supported(struct bnx2x *bp, u32 switch_cfg) BNX2X_DEV_INFO("ext_phy_type 0x%x (Direct)\n", ext_phy_type); - bp->supported |= (SUPPORTED_10baseT_Half | - SUPPORTED_10baseT_Full | - SUPPORTED_100baseT_Half | - SUPPORTED_100baseT_Full | - SUPPORTED_1000baseT_Full | - SUPPORTED_2500baseX_Full | - SUPPORTED_10000baseT_Full | - SUPPORTED_TP | SUPPORTED_FIBRE | - SUPPORTED_Autoneg | - SUPPORTED_Pause | - SUPPORTED_Asym_Pause); + bp->port.supported |= (SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Full | + SUPPORTED_2500baseX_Full | + SUPPORTED_10000baseT_Full | + SUPPORTED_TP | + SUPPORTED_FIBRE | + SUPPORTED_Autoneg | + SUPPORTED_Pause | + SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705: BNX2X_DEV_INFO("ext_phy_type 0x%x (8705)\n", - ext_phy_type); + ext_phy_type); - bp->supported |= (SUPPORTED_10000baseT_Full | - SUPPORTED_FIBRE | - SUPPORTED_Pause | - SUPPORTED_Asym_Pause); + bp->port.supported |= (SUPPORTED_10000baseT_Full | + SUPPORTED_FIBRE | + SUPPORTED_Pause | + SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706: BNX2X_DEV_INFO("ext_phy_type 0x%x (8706)\n", ext_phy_type); - bp->supported |= (SUPPORTED_10000baseT_Full | - SUPPORTED_1000baseT_Full | - SUPPORTED_Autoneg | - SUPPORTED_FIBRE | - SUPPORTED_Pause | - SUPPORTED_Asym_Pause); + bp->port.supported |= (SUPPORTED_10000baseT_Full | + SUPPORTED_1000baseT_Full | + SUPPORTED_FIBRE | + SUPPORTED_Pause | + SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: BNX2X_DEV_INFO("ext_phy_type 0x%x (8072)\n", ext_phy_type); - bp->supported |= (SUPPORTED_10000baseT_Full | - SUPPORTED_1000baseT_Full | - SUPPORTED_FIBRE | - SUPPORTED_Autoneg | - SUPPORTED_Pause | - SUPPORTED_Asym_Pause); + bp->port.supported |= (SUPPORTED_10000baseT_Full | + SUPPORTED_1000baseT_Full | + SUPPORTED_FIBRE | + SUPPORTED_Autoneg | + SUPPORTED_Pause | + SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: BNX2X_DEV_INFO("ext_phy_type 0x%x (8073)\n", ext_phy_type); - bp->supported |= (SUPPORTED_10000baseT_Full | - SUPPORTED_2500baseX_Full | - SUPPORTED_1000baseT_Full | - SUPPORTED_FIBRE | - SUPPORTED_Autoneg | - SUPPORTED_Pause | - SUPPORTED_Asym_Pause); + bp->port.supported |= (SUPPORTED_10000baseT_Full | + SUPPORTED_2500baseX_Full | + SUPPORTED_1000baseT_Full | + SUPPORTED_FIBRE | + SUPPORTED_Autoneg | + SUPPORTED_Pause | + SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101: BNX2X_DEV_INFO("ext_phy_type 0x%x (SFX7101)\n", ext_phy_type); - bp->supported |= (SUPPORTED_10000baseT_Full | - SUPPORTED_TP | - SUPPORTED_Autoneg | - SUPPORTED_Pause | - SUPPORTED_Asym_Pause); + bp->port.supported |= (SUPPORTED_10000baseT_Full | + SUPPORTED_TP | + SUPPORTED_Autoneg | + SUPPORTED_Pause | + SUPPORTED_Asym_Pause); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE: @@ -5164,61 +6039,61 @@ static void bnx2x_link_settings_supported(struct bnx2x *bp, u32 switch_cfg) return; } - bp->phy_addr = REG_RD(bp, NIG_REG_XGXS0_CTRL_PHY_ADDR + - port*0x18); - BNX2X_DEV_INFO("phy_addr 0x%x\n", bp->phy_addr); + bp->port.phy_addr = REG_RD(bp, NIG_REG_XGXS0_CTRL_PHY_ADDR + + port*0x18); + BNX2X_DEV_INFO("phy_addr 0x%x\n", bp->port.phy_addr); break; default: BNX2X_ERR("BAD switch_cfg link_config 0x%x\n", - bp->link_config); + bp->port.link_config); return; } - bp->link_params.phy_addr = bp->phy_addr; + bp->link_params.phy_addr = bp->port.phy_addr; /* mask what we support according to speed_cap_mask */ if (!(bp->link_params.speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_HALF)) - bp->supported &= ~SUPPORTED_10baseT_Half; + bp->port.supported &= ~SUPPORTED_10baseT_Half; if (!(bp->link_params.speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_FULL)) - bp->supported &= ~SUPPORTED_10baseT_Full; + bp->port.supported &= ~SUPPORTED_10baseT_Full; if (!(bp->link_params.speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_HALF)) - bp->supported &= ~SUPPORTED_100baseT_Half; + bp->port.supported &= ~SUPPORTED_100baseT_Half; if (!(bp->link_params.speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_100M_FULL)) - bp->supported &= ~SUPPORTED_100baseT_Full; + bp->port.supported &= ~SUPPORTED_100baseT_Full; if (!(bp->link_params.speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_1G)) - bp->supported &= ~(SUPPORTED_1000baseT_Half | - SUPPORTED_1000baseT_Full); + bp->port.supported &= ~(SUPPORTED_1000baseT_Half | + SUPPORTED_1000baseT_Full); if (!(bp->link_params.speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_2_5G)) - bp->supported &= ~SUPPORTED_2500baseX_Full; + bp->port.supported &= ~SUPPORTED_2500baseX_Full; if (!(bp->link_params.speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_10G)) - bp->supported &= ~SUPPORTED_10000baseT_Full; + bp->port.supported &= ~SUPPORTED_10000baseT_Full; - BNX2X_DEV_INFO("supported 0x%x\n", bp->supported); + BNX2X_DEV_INFO("supported 0x%x\n", bp->port.supported); } -static void bnx2x_link_settings_requested(struct bnx2x *bp) +static void __devinit bnx2x_link_settings_requested(struct bnx2x *bp) { bp->link_params.req_duplex = DUPLEX_FULL; - switch (bp->link_config & PORT_FEATURE_LINK_SPEED_MASK) { + switch (bp->port.link_config & PORT_FEATURE_LINK_SPEED_MASK) { case PORT_FEATURE_LINK_SPEED_AUTO: - if (bp->supported & SUPPORTED_Autoneg) { + if (bp->port.supported & SUPPORTED_Autoneg) { bp->link_params.req_line_speed = SPEED_AUTO_NEG; - bp->advertising = bp->supported; + bp->port.advertising = bp->port.supported; } else { u32 ext_phy_type = XGXS_EXT_PHY_TYPE(bp->link_params.ext_phy_config); @@ -5229,7 +6104,7 @@ static void bnx2x_link_settings_requested(struct bnx2x *bp) PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706)) { /* force 10G, no AN */ bp->link_params.req_line_speed = SPEED_10000; - bp->advertising = + bp->port.advertising = (ADVERTISED_10000baseT_Full | ADVERTISED_FIBRE); break; @@ -5237,98 +6112,98 @@ static void bnx2x_link_settings_requested(struct bnx2x *bp) BNX2X_ERR("NVRAM config error. " "Invalid link_config 0x%x" " Autoneg not supported\n", - bp->link_config); + bp->port.link_config); return; } break; case PORT_FEATURE_LINK_SPEED_10M_FULL: - if (bp->supported & SUPPORTED_10baseT_Full) { + if (bp->port.supported & SUPPORTED_10baseT_Full) { bp->link_params.req_line_speed = SPEED_10; - bp->advertising = (ADVERTISED_10baseT_Full | - ADVERTISED_TP); + bp->port.advertising = (ADVERTISED_10baseT_Full | + ADVERTISED_TP); } else { BNX2X_ERR("NVRAM config error. " "Invalid link_config 0x%x" " speed_cap_mask 0x%x\n", - bp->link_config, + bp->port.link_config, bp->link_params.speed_cap_mask); return; } break; case PORT_FEATURE_LINK_SPEED_10M_HALF: - if (bp->supported & SUPPORTED_10baseT_Half) { + if (bp->port.supported & SUPPORTED_10baseT_Half) { bp->link_params.req_line_speed = SPEED_10; bp->link_params.req_duplex = DUPLEX_HALF; - bp->advertising = (ADVERTISED_10baseT_Half | - ADVERTISED_TP); + bp->port.advertising = (ADVERTISED_10baseT_Half | + ADVERTISED_TP); } else { BNX2X_ERR("NVRAM config error. " "Invalid link_config 0x%x" " speed_cap_mask 0x%x\n", - bp->link_config, + bp->port.link_config, bp->link_params.speed_cap_mask); return; } break; case PORT_FEATURE_LINK_SPEED_100M_FULL: - if (bp->supported & SUPPORTED_100baseT_Full) { + if (bp->port.supported & SUPPORTED_100baseT_Full) { bp->link_params.req_line_speed = SPEED_100; - bp->advertising = (ADVERTISED_100baseT_Full | - ADVERTISED_TP); + bp->port.advertising = (ADVERTISED_100baseT_Full | + ADVERTISED_TP); } else { BNX2X_ERR("NVRAM config error. " "Invalid link_config 0x%x" " speed_cap_mask 0x%x\n", - bp->link_config, + bp->port.link_config, bp->link_params.speed_cap_mask); return; } break; case PORT_FEATURE_LINK_SPEED_100M_HALF: - if (bp->supported & SUPPORTED_100baseT_Half) { + if (bp->port.supported & SUPPORTED_100baseT_Half) { bp->link_params.req_line_speed = SPEED_100; bp->link_params.req_duplex = DUPLEX_HALF; - bp->advertising = (ADVERTISED_100baseT_Half | - ADVERTISED_TP); + bp->port.advertising = (ADVERTISED_100baseT_Half | + ADVERTISED_TP); } else { BNX2X_ERR("NVRAM config error. " "Invalid link_config 0x%x" " speed_cap_mask 0x%x\n", - bp->link_config, + bp->port.link_config, bp->link_params.speed_cap_mask); return; } break; case PORT_FEATURE_LINK_SPEED_1G: - if (bp->supported & SUPPORTED_1000baseT_Full) { + if (bp->port.supported & SUPPORTED_1000baseT_Full) { bp->link_params.req_line_speed = SPEED_1000; - bp->advertising = (ADVERTISED_1000baseT_Full | - ADVERTISED_TP); + bp->port.advertising = (ADVERTISED_1000baseT_Full | + ADVERTISED_TP); } else { BNX2X_ERR("NVRAM config error. " "Invalid link_config 0x%x" " speed_cap_mask 0x%x\n", - bp->link_config, + bp->port.link_config, bp->link_params.speed_cap_mask); return; } break; case PORT_FEATURE_LINK_SPEED_2_5G: - if (bp->supported & SUPPORTED_2500baseX_Full) { + if (bp->port.supported & SUPPORTED_2500baseX_Full) { bp->link_params.req_line_speed = SPEED_2500; - bp->advertising = (ADVERTISED_2500baseX_Full | - ADVERTISED_TP); + bp->port.advertising = (ADVERTISED_2500baseX_Full | + ADVERTISED_TP); } else { BNX2X_ERR("NVRAM config error. " "Invalid link_config 0x%x" " speed_cap_mask 0x%x\n", - bp->link_config, + bp->port.link_config, bp->link_params.speed_cap_mask); return; } @@ -5337,15 +6212,15 @@ static void bnx2x_link_settings_requested(struct bnx2x *bp) case PORT_FEATURE_LINK_SPEED_10G_CX4: case PORT_FEATURE_LINK_SPEED_10G_KX4: case PORT_FEATURE_LINK_SPEED_10G_KR: - if (bp->supported & SUPPORTED_10000baseT_Full) { + if (bp->port.supported & SUPPORTED_10000baseT_Full) { bp->link_params.req_line_speed = SPEED_10000; - bp->advertising = (ADVERTISED_10000baseT_Full | - ADVERTISED_FIBRE); + bp->port.advertising = (ADVERTISED_10000baseT_Full | + ADVERTISED_FIBRE); } else { BNX2X_ERR("NVRAM config error. " "Invalid link_config 0x%x" " speed_cap_mask 0x%x\n", - bp->link_config, + bp->port.link_config, bp->link_params.speed_cap_mask); return; } @@ -5354,64 +6229,33 @@ static void bnx2x_link_settings_requested(struct bnx2x *bp) default: BNX2X_ERR("NVRAM config error. " "BAD link speed link_config 0x%x\n", - bp->link_config); + bp->port.link_config); bp->link_params.req_line_speed = SPEED_AUTO_NEG; - bp->advertising = bp->supported; + bp->port.advertising = bp->port.supported; break; } - bp->link_params.req_flow_ctrl = (bp->link_config & - PORT_FEATURE_FLOW_CONTROL_MASK); + bp->link_params.req_flow_ctrl = (bp->port.link_config & + PORT_FEATURE_FLOW_CONTROL_MASK); if ((bp->link_params.req_flow_ctrl == FLOW_CTRL_AUTO) && - (!bp->supported & SUPPORTED_Autoneg)) + (!bp->port.supported & SUPPORTED_Autoneg)) bp->link_params.req_flow_ctrl = FLOW_CTRL_NONE; BNX2X_DEV_INFO("req_line_speed %d req_duplex %d req_flow_ctrl 0x%x" " advertising 0x%x\n", bp->link_params.req_line_speed, bp->link_params.req_duplex, - bp->link_params.req_flow_ctrl, bp->advertising); + bp->link_params.req_flow_ctrl, bp->port.advertising); } -static void bnx2x_get_hwinfo(struct bnx2x *bp) +static void __devinit bnx2x_get_port_hwinfo(struct bnx2x *bp) { - u32 val, val2, val3, val4, id; - int port = bp->port; - - bp->shmem_base = REG_RD(bp, MISC_REG_SHARED_MEM_ADDR); - BNX2X_DEV_INFO("shmem offset is %x\n", bp->shmem_base); - - /* Get the chip revision id and number. */ - /* chip num:16-31, rev:12-15, metal:4-11, bond_id:0-3 */ - val = REG_RD(bp, MISC_REG_CHIP_NUM); - id = ((val & 0xffff) << 16); - val = REG_RD(bp, MISC_REG_CHIP_REV); - id |= ((val & 0xf) << 12); - val = REG_RD(bp, MISC_REG_CHIP_METAL); - id |= ((val & 0xff) << 4); - REG_RD(bp, MISC_REG_BOND_ID); - id |= (val & 0xf); - bp->chip_id = id; - BNX2X_DEV_INFO("chip ID is %x\n", id); + int port = BP_PORT(bp); + u32 val, val2; bp->link_params.bp = bp; + bp->link_params.port = port; - if (!bp->shmem_base || (bp->shmem_base != 0xAF900)) { - BNX2X_DEV_INFO("MCP not active\n"); - nomcp = 1; - goto set_mac; - } - - val = SHMEM_RD(bp, validity_map[port]); - if ((val & (SHR_MEM_VALIDITY_DEV_INFO | SHR_MEM_VALIDITY_MB)) - != (SHR_MEM_VALIDITY_DEV_INFO | SHR_MEM_VALIDITY_MB)) - BNX2X_ERR("BAD MCP validity signature\n"); - - bp->fw_seq = (SHMEM_RD(bp, func_mb[port].drv_mb_header) & - DRV_MSG_SEQ_NUMBER_MASK); - - bp->hw_config = SHMEM_RD(bp, dev_info.shared_hw_config.config); - bp->board = SHMEM_RD(bp, dev_info.shared_hw_config.board); bp->link_params.serdes_config = SHMEM_RD(bp, dev_info.port_hw_config[port].serdes_config); bp->link_params.lane_config = @@ -5423,19 +6267,18 @@ static void bnx2x_get_hwinfo(struct bnx2x *bp) SHMEM_RD(bp, dev_info.port_hw_config[port].speed_capability_mask); - bp->link_config = + bp->port.link_config = SHMEM_RD(bp, dev_info.port_feature_config[port].link_config); - BNX2X_DEV_INFO("serdes_config (%08x) lane_config (%08x)\n" - KERN_INFO " ext_phy_config (%08x) speed_cap_mask (%08x)" - " link_config (%08x)\n", + BNX2X_DEV_INFO("serdes_config 0x%08x lane_config 0x%08x\n" + KERN_INFO " ext_phy_config 0x%08x speed_cap_mask 0x%08x" + " link_config 0x%08x\n", bp->link_params.serdes_config, bp->link_params.lane_config, bp->link_params.ext_phy_config, - bp->link_params.speed_cap_mask, - bp->link_config); + bp->link_params.speed_cap_mask, bp->port.link_config); - bp->link_params.switch_cfg = (bp->link_config & + bp->link_params.switch_cfg = (bp->port.link_config & PORT_FEATURE_CONNECTED_SWITCH_MASK); bnx2x_link_settings_supported(bp, bp->link_params.switch_cfg); @@ -5451,43 +6294,126 @@ static void bnx2x_get_hwinfo(struct bnx2x *bp) bp->dev->dev_addr[5] = (u8)(val & 0xff); memcpy(bp->link_params.mac_addr, bp->dev->dev_addr, ETH_ALEN); memcpy(bp->dev->perm_addr, bp->dev->dev_addr, ETH_ALEN); +} + +static int __devinit bnx2x_get_hwinfo(struct bnx2x *bp) +{ + int func = BP_FUNC(bp); + u32 val, val2; + int rc = 0; + bnx2x_get_common_hwinfo(bp); + bp->e1hov = 0; + bp->e1hmf = 0; + if (CHIP_IS_E1H(bp)) { + bp->mf_config = + SHMEM_RD(bp, mf_cfg.func_mf_config[func].config); - val = SHMEM_RD(bp, dev_info.shared_hw_config.part_num); - val2 = SHMEM_RD(bp, dev_info.shared_hw_config.part_num[4]); - val3 = SHMEM_RD(bp, dev_info.shared_hw_config.part_num[8]); - val4 = SHMEM_RD(bp, dev_info.shared_hw_config.part_num[12]); + val = + (SHMEM_RD(bp, mf_cfg.func_mf_config[func].e1hov_tag) & + FUNC_MF_CFG_E1HOV_TAG_MASK); + if (val != FUNC_MF_CFG_E1HOV_TAG_DEFAULT) { - printk(KERN_INFO PFX "part number %X-%X-%X-%X\n", - val, val2, val3, val4); + bp->e1hov = val; + bp->e1hmf = 1; + BNX2X_DEV_INFO("MF mode E1HOV for func %d is %d " + "(0x%04x)\n", + func, bp->e1hov, bp->e1hov); + } else { + BNX2X_DEV_INFO("Single function mode\n"); + if (BP_E1HVN(bp)) { + BNX2X_ERR("!!! No valid E1HOV for func %d," + " aborting\n", func); + rc = -EPERM; + } + } + } - /* bc ver */ - if (!nomcp) { - bp->bc_ver = val = ((SHMEM_RD(bp, dev_info.bc_rev)) >> 8); - BNX2X_DEV_INFO("bc_ver %X\n", val); - if (val < BNX2X_BC_VER) { - /* for now only warn - * later we might need to enforce this */ - BNX2X_ERR("This driver needs bc_ver %X but found %X," - " please upgrade BC\n", BNX2X_BC_VER, val); + if (!BP_NOMCP(bp)) { + bnx2x_get_port_hwinfo(bp); + + bp->fw_seq = (SHMEM_RD(bp, func_mb[func].drv_mb_header) & + DRV_MSG_SEQ_NUMBER_MASK); + BNX2X_DEV_INFO("fw_seq 0x%08x\n", bp->fw_seq); + } + + if (IS_E1HMF(bp)) { + val2 = SHMEM_RD(bp, mf_cfg.func_mf_config[func].mac_upper); + val = SHMEM_RD(bp, mf_cfg.func_mf_config[func].mac_lower); + if ((val2 != FUNC_MF_CFG_UPPERMAC_DEFAULT) && + (val != FUNC_MF_CFG_LOWERMAC_DEFAULT)) { + bp->dev->dev_addr[0] = (u8)(val2 >> 8 & 0xff); + bp->dev->dev_addr[1] = (u8)(val2 & 0xff); + bp->dev->dev_addr[2] = (u8)(val >> 24 & 0xff); + bp->dev->dev_addr[3] = (u8)(val >> 16 & 0xff); + bp->dev->dev_addr[4] = (u8)(val >> 8 & 0xff); + bp->dev->dev_addr[5] = (u8)(val & 0xff); + memcpy(bp->link_params.mac_addr, bp->dev->dev_addr, + ETH_ALEN); + memcpy(bp->dev->perm_addr, bp->dev->dev_addr, + ETH_ALEN); } - } else { - bp->bc_ver = 0; + + return rc; } - val = REG_RD(bp, MCP_REG_MCPR_NVM_CFG4); - bp->flash_size = (NVRAM_1MB_SIZE << (val & MCPR_NVM_CFG4_FLASH_SIZE)); - BNX2X_DEV_INFO("flash_size 0x%x (%d)\n", - bp->flash_size, bp->flash_size); + if (BP_NOMCP(bp)) { + /* only supposed to happen on emulation/FPGA */ + BNX2X_ERR("warning rendom MAC workaround active\n"); + random_ether_addr(bp->dev->dev_addr); + memcpy(bp->dev->perm_addr, bp->dev->dev_addr, ETH_ALEN); + } - return; + return rc; +} + +static int __devinit bnx2x_init_bp(struct bnx2x *bp) +{ + int func = BP_FUNC(bp); + int rc; + + if (nomcp) + bp->flags |= NO_MCP_FLAG; -set_mac: /* only supposed to happen on emulation/FPGA */ - BNX2X_ERR("warning rendom MAC workaround active\n"); - random_ether_addr(bp->dev->dev_addr); - memcpy(bp->dev->perm_addr, bp->dev->dev_addr, 6); + mutex_init(&bp->port.phy_mutex); + INIT_WORK(&bp->sp_task, bnx2x_sp_task); + INIT_WORK(&bp->reset_task, bnx2x_reset_task); + + rc = bnx2x_get_hwinfo(bp); + + /* need to reset chip if undi was active */ + if (!BP_NOMCP(bp)) + bnx2x_undi_unload(bp); + + if (CHIP_REV_IS_FPGA(bp)) + printk(KERN_ERR PFX "FPGA detected\n"); + + if (BP_NOMCP(bp) && (func == 0)) + printk(KERN_ERR PFX + "MCP disabled, must load devices in order!\n"); + + bp->tx_ring_size = MAX_TX_AVAIL; + bp->rx_ring_size = MAX_RX_AVAIL; + + bp->rx_csum = 1; + bp->rx_offset = 0; + + bp->tx_ticks = 50; + bp->rx_ticks = 25; + + bp->stats_ticks = 1000000 & 0xffff00; + + bp->timer_interval = (CHIP_REV_IS_SLOW(bp) ? 5*HZ : HZ); + bp->current_interval = (poll ? poll : bp->timer_interval); + + init_timer(&bp->timer); + bp->timer.expires = jiffies + bp->current_interval; + bp->timer.data = (unsigned long) bp; + bp->timer.function = bnx2x_timer; + + return rc; } /* @@ -5500,8 +6426,8 @@ static int bnx2x_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { struct bnx2x *bp = netdev_priv(dev); - cmd->supported = bp->supported; - cmd->advertising = bp->advertising; + cmd->supported = bp->port.supported; + cmd->advertising = bp->port.advertising; if (netif_carrier_ok(dev)) { cmd->speed = bp->link_vars.line_speed; @@ -5510,6 +6436,14 @@ static int bnx2x_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) cmd->speed = bp->link_params.req_line_speed; cmd->duplex = bp->link_params.req_duplex; } + if (IS_E1HMF(bp)) { + u16 vn_max_rate; + + vn_max_rate = ((bp->mf_config & FUNC_MF_CFG_MAX_BW_MASK) >> + FUNC_MF_CFG_MAX_BW_SHIFT) * 100; + if (vn_max_rate < cmd->speed) + cmd->speed = vn_max_rate; + } if (bp->link_params.switch_cfg == SWITCH_CFG_10G) { u32 ext_phy_type = @@ -5541,7 +6475,7 @@ static int bnx2x_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) } else cmd->port = PORT_TP; - cmd->phy_address = bp->phy_addr; + cmd->phy_address = bp->port.phy_addr; cmd->transceiver = XCVR_INTERNAL; if (bp->link_params.req_line_speed == SPEED_AUTO_NEG) @@ -5568,6 +6502,9 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) struct bnx2x *bp = netdev_priv(dev); u32 advertising; + if (IS_E1HMF(bp)) + return 0; + DP(NETIF_MSG_LINK, "ethtool_cmd: cmd %d\n" DP_LEVEL " supported 0x%x advertising 0x%x speed %d\n" DP_LEVEL " duplex %d port %d phy_address %d transceiver %d\n" @@ -5577,24 +6514,25 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) cmd->autoneg, cmd->maxtxpkt, cmd->maxrxpkt); if (cmd->autoneg == AUTONEG_ENABLE) { - if (!(bp->supported & SUPPORTED_Autoneg)) { - DP(NETIF_MSG_LINK, "Aotoneg not supported\n"); + if (!(bp->port.supported & SUPPORTED_Autoneg)) { + DP(NETIF_MSG_LINK, "Autoneg not supported\n"); return -EINVAL; } /* advertise the requested speed and duplex if supported */ - cmd->advertising &= bp->supported; + cmd->advertising &= bp->port.supported; bp->link_params.req_line_speed = SPEED_AUTO_NEG; bp->link_params.req_duplex = DUPLEX_FULL; - bp->advertising |= (ADVERTISED_Autoneg | cmd->advertising); + bp->port.advertising |= (ADVERTISED_Autoneg | + cmd->advertising); } else { /* forced speed */ /* advertise the requested speed and duplex if supported */ switch (cmd->speed) { case SPEED_10: if (cmd->duplex == DUPLEX_FULL) { - if (!(bp->supported & + if (!(bp->port.supported & SUPPORTED_10baseT_Full)) { DP(NETIF_MSG_LINK, "10M full not supported\n"); @@ -5604,7 +6542,7 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) advertising = (ADVERTISED_10baseT_Full | ADVERTISED_TP); } else { - if (!(bp->supported & + if (!(bp->port.supported & SUPPORTED_10baseT_Half)) { DP(NETIF_MSG_LINK, "10M half not supported\n"); @@ -5618,7 +6556,7 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) case SPEED_100: if (cmd->duplex == DUPLEX_FULL) { - if (!(bp->supported & + if (!(bp->port.supported & SUPPORTED_100baseT_Full)) { DP(NETIF_MSG_LINK, "100M full not supported\n"); @@ -5628,7 +6566,7 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) advertising = (ADVERTISED_100baseT_Full | ADVERTISED_TP); } else { - if (!(bp->supported & + if (!(bp->port.supported & SUPPORTED_100baseT_Half)) { DP(NETIF_MSG_LINK, "100M half not supported\n"); @@ -5646,7 +6584,7 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) return -EINVAL; } - if (!(bp->supported & SUPPORTED_1000baseT_Full)) { + if (!(bp->port.supported & SUPPORTED_1000baseT_Full)) { DP(NETIF_MSG_LINK, "1G full not supported\n"); return -EINVAL; } @@ -5662,7 +6600,7 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) return -EINVAL; } - if (!(bp->supported & SUPPORTED_2500baseX_Full)) { + if (!(bp->port.supported & SUPPORTED_2500baseX_Full)) { DP(NETIF_MSG_LINK, "2.5G full not supported\n"); return -EINVAL; @@ -5678,7 +6616,7 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) return -EINVAL; } - if (!(bp->supported & SUPPORTED_10000baseT_Full)) { + if (!(bp->port.supported & SUPPORTED_10000baseT_Full)) { DP(NETIF_MSG_LINK, "10G full not supported\n"); return -EINVAL; } @@ -5694,16 +6632,18 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) bp->link_params.req_line_speed = cmd->speed; bp->link_params.req_duplex = cmd->duplex; - bp->advertising = advertising; + bp->port.advertising = advertising; } DP(NETIF_MSG_LINK, "req_line_speed %d\n" DP_LEVEL " req_duplex %d advertising 0x%x\n", bp->link_params.req_line_speed, bp->link_params.req_duplex, - bp->advertising); + bp->port.advertising); - bnx2x_stop_stats(bp); - bnx2x_link_set(bp); + if (netif_running(dev)) { + bnx2x_stop_stats(bp); + bnx2x_link_set(bp); + } return 0; } @@ -5720,21 +6660,23 @@ static void bnx2x_get_drvinfo(struct net_device *dev, strcpy(info->version, DRV_MODULE_VERSION); phy_fw_ver[0] = '\0'; - bnx2x_phy_hw_lock(bp); - bnx2x_get_ext_phy_fw_version(&bp->link_params, - (bp->state != BNX2X_STATE_CLOSED), - phy_fw_ver, PHY_FW_VER_LEN); - bnx2x_phy_hw_unlock(bp); + if (bp->port.pmf) { + bnx2x_phy_hw_lock(bp); + bnx2x_get_ext_phy_fw_version(&bp->link_params, + (bp->state != BNX2X_STATE_CLOSED), + phy_fw_ver, PHY_FW_VER_LEN); + bnx2x_phy_hw_unlock(bp); + } snprintf(info->fw_version, 32, "%d.%d.%d:%d BC:%x%s%s", BCM_5710_FW_MAJOR_VERSION, BCM_5710_FW_MINOR_VERSION, BCM_5710_FW_REVISION_VERSION, - BCM_5710_FW_COMPILE_FLAGS, bp->bc_ver, + BCM_5710_FW_COMPILE_FLAGS, bp->common.bc_ver, ((phy_fw_ver[0] != '\0')? " PHY:":""), phy_fw_ver); strcpy(info->bus_info, pci_name(bp->pdev)); info->n_stats = BNX2X_NUM_STATS; info->testinfo_len = BNX2X_NUM_TESTS; - info->eedump_len = bp->flash_size; + info->eedump_len = bp->common.flash_size; info->regdump_len = 0; } @@ -5767,9 +6709,9 @@ static int bnx2x_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) return -EINVAL; bp->wol = 1; - } else { + } else bp->wol = 0; - } + return 0; } @@ -5792,13 +6734,13 @@ static int bnx2x_nway_reset(struct net_device *dev) { struct bnx2x *bp = netdev_priv(dev); - if (bp->state != BNX2X_STATE_OPEN) { - DP(NETIF_MSG_PROBE, "state is %x, returning\n", bp->state); - return -EAGAIN; - } + if (!bp->port.pmf) + return 0; - bnx2x_stop_stats(bp); - bnx2x_link_set(bp); + if (netif_running(dev)) { + bnx2x_stop_stats(bp); + bnx2x_link_set(bp); + } return 0; } @@ -5807,12 +6749,12 @@ static int bnx2x_get_eeprom_len(struct net_device *dev) { struct bnx2x *bp = netdev_priv(dev); - return bp->flash_size; + return bp->common.flash_size; } static int bnx2x_acquire_nvram_lock(struct bnx2x *bp) { - int port = bp->port; + int port = BP_PORT(bp); int count, i; u32 val = 0; @@ -5834,7 +6776,7 @@ static int bnx2x_acquire_nvram_lock(struct bnx2x *bp) } if (!(val & (MCPR_NVM_SW_ARB_ARB_ARB1 << port))) { - DP(NETIF_MSG_NVM, "cannot get access to nvram interface\n"); + DP(BNX2X_MSG_NVM, "cannot get access to nvram interface\n"); return -EBUSY; } @@ -5843,7 +6785,7 @@ static int bnx2x_acquire_nvram_lock(struct bnx2x *bp) static int bnx2x_release_nvram_lock(struct bnx2x *bp) { - int port = bp->port; + int port = BP_PORT(bp); int count, i; u32 val = 0; @@ -5865,7 +6807,7 @@ static int bnx2x_release_nvram_lock(struct bnx2x *bp) } if (val & (MCPR_NVM_SW_ARB_ARB_ARB1 << port)) { - DP(NETIF_MSG_NVM, "cannot free access to nvram interface\n"); + DP(BNX2X_MSG_NVM, "cannot free access to nvram interface\n"); return -EBUSY; } @@ -5929,7 +6871,6 @@ static int bnx2x_nvram_read_dword(struct bnx2x *bp, u32 offset, u32 *ret_val, if (val & MCPR_NVM_COMMAND_DONE) { val = REG_RD(bp, MCP_REG_MCPR_NVM_READ); - DP(NETIF_MSG_NVM, "val 0x%08x\n", val); /* we read nvram data in cpu order * but ethtool sees it as an array of bytes * converting to big-endian will do the work */ @@ -5951,16 +6892,16 @@ static int bnx2x_nvram_read(struct bnx2x *bp, u32 offset, u8 *ret_buf, u32 val; if ((offset & 0x03) || (buf_size & 0x03) || (buf_size == 0)) { - DP(NETIF_MSG_NVM, + DP(BNX2X_MSG_NVM, "Invalid parameter: offset 0x%x buf_size 0x%x\n", offset, buf_size); return -EINVAL; } - if (offset + buf_size > bp->flash_size) { - DP(NETIF_MSG_NVM, "Invalid parameter: offset (0x%x) +" + if (offset + buf_size > bp->common.flash_size) { + DP(BNX2X_MSG_NVM, "Invalid parameter: offset (0x%x) +" " buf_size (0x%x) > flash_size (0x%x)\n", - offset, buf_size, bp->flash_size); + offset, buf_size, bp->common.flash_size); return -EINVAL; } @@ -6004,7 +6945,7 @@ static int bnx2x_get_eeprom(struct net_device *dev, struct bnx2x *bp = netdev_priv(dev); int rc; - DP(NETIF_MSG_NVM, "ethtool_eeprom: cmd %d\n" + DP(BNX2X_MSG_NVM, "ethtool_eeprom: cmd %d\n" DP_LEVEL " magic 0x%x offset 0x%x (%d) len 0x%x (%d)\n", eeprom->cmd, eeprom->magic, eeprom->offset, eeprom->offset, eeprom->len, eeprom->len); @@ -6066,10 +7007,10 @@ static int bnx2x_nvram_write1(struct bnx2x *bp, u32 offset, u8 *data_buf, u32 align_offset; u32 val; - if (offset + buf_size > bp->flash_size) { - DP(NETIF_MSG_NVM, "Invalid parameter: offset (0x%x) +" + if (offset + buf_size > bp->common.flash_size) { + DP(BNX2X_MSG_NVM, "Invalid parameter: offset (0x%x) +" " buf_size (0x%x) > flash_size (0x%x)\n", - offset, buf_size, bp->flash_size); + offset, buf_size, bp->common.flash_size); return -EINVAL; } @@ -6093,8 +7034,6 @@ static int bnx2x_nvram_write1(struct bnx2x *bp, u32 offset, u8 *data_buf, * convert it back to cpu order */ val = be32_to_cpu(val); - DP(NETIF_MSG_NVM, "val 0x%08x\n", val); - rc = bnx2x_nvram_write_dword(bp, align_offset, val, cmd_flags); } @@ -6114,21 +7053,20 @@ static int bnx2x_nvram_write(struct bnx2x *bp, u32 offset, u8 *data_buf, u32 val; u32 written_so_far; - if (buf_size == 1) { /* ethtool */ + if (buf_size == 1) /* ethtool */ return bnx2x_nvram_write1(bp, offset, data_buf, buf_size); - } if ((offset & 0x03) || (buf_size & 0x03) || (buf_size == 0)) { - DP(NETIF_MSG_NVM, + DP(BNX2X_MSG_NVM, "Invalid parameter: offset 0x%x buf_size 0x%x\n", offset, buf_size); return -EINVAL; } - if (offset + buf_size > bp->flash_size) { - DP(NETIF_MSG_NVM, "Invalid parameter: offset (0x%x) +" + if (offset + buf_size > bp->common.flash_size) { + DP(BNX2X_MSG_NVM, "Invalid parameter: offset (0x%x) +" " buf_size (0x%x) > flash_size (0x%x)\n", - offset, buf_size, bp->flash_size); + offset, buf_size, bp->common.flash_size); return -EINVAL; } @@ -6151,7 +7089,6 @@ static int bnx2x_nvram_write(struct bnx2x *bp, u32 offset, u8 *data_buf, cmd_flags |= MCPR_NVM_COMMAND_FIRST; memcpy(&val, data_buf, 4); - DP(NETIF_MSG_NVM, "val 0x%08x\n", val); rc = bnx2x_nvram_write_dword(bp, offset, val, cmd_flags); @@ -6175,7 +7112,7 @@ static int bnx2x_set_eeprom(struct net_device *dev, struct bnx2x *bp = netdev_priv(dev); int rc; - DP(NETIF_MSG_NVM, "ethtool_eeprom: cmd %d\n" + DP(BNX2X_MSG_NVM, "ethtool_eeprom: cmd %d\n" DP_LEVEL " magic 0x%x offset 0x%x (%d) len 0x%x (%d)\n", eeprom->cmd, eeprom->magic, eeprom->offset, eeprom->offset, eeprom->len, eeprom->len); @@ -6183,20 +7120,23 @@ static int bnx2x_set_eeprom(struct net_device *dev, /* parameters already validated in ethtool_set_eeprom */ /* If the magic number is PHY (0x00504859) upgrade the PHY FW */ - if (eeprom->magic == 0x00504859) { - - bnx2x_phy_hw_lock(bp); - rc = bnx2x_flash_download(bp, bp->port, - bp->link_params.ext_phy_config, - (bp->state != BNX2X_STATE_CLOSED), - eebuf, eeprom->len); - rc |= bnx2x_link_reset(&bp->link_params, - &bp->link_vars); - rc |= bnx2x_phy_init(&bp->link_params, - &bp->link_vars); - bnx2x_phy_hw_unlock(bp); - - } else + if (eeprom->magic == 0x00504859) + if (bp->port.pmf) { + + bnx2x_phy_hw_lock(bp); + rc = bnx2x_flash_download(bp, BP_PORT(bp), + bp->link_params.ext_phy_config, + (bp->state != BNX2X_STATE_CLOSED), + eebuf, eeprom->len); + rc |= bnx2x_link_reset(&bp->link_params, + &bp->link_vars); + rc |= bnx2x_phy_init(&bp->link_params, + &bp->link_vars); + bnx2x_phy_hw_unlock(bp); + + } else /* Only the PMF can access the PHY */ + return -EINVAL; + else rc = bnx2x_nvram_write(bp, eeprom->offset, eebuf, eeprom->len); return rc; @@ -6234,7 +7174,7 @@ static int bnx2x_set_coalesce(struct net_device *dev, bp->stats_ticks = 0xffff00; bp->stats_ticks &= 0xffff00; - if (netif_running(bp->dev)) + if (netif_running(dev)) bnx2x_update_coalesce(bp); return 0; @@ -6261,6 +7201,7 @@ static int bnx2x_set_ringparam(struct net_device *dev, struct ethtool_ringparam *ering) { struct bnx2x *bp = netdev_priv(dev); + int rc = 0; if ((ering->rx_pending > MAX_RX_AVAIL) || (ering->tx_pending > MAX_TX_AVAIL) || @@ -6270,12 +7211,12 @@ static int bnx2x_set_ringparam(struct net_device *dev, bp->rx_ring_size = ering->rx_pending; bp->tx_ring_size = ering->tx_pending; - if (netif_running(bp->dev)) { - bnx2x_nic_unload(bp, 0); - bnx2x_nic_load(bp, 0); + if (netif_running(dev)) { + bnx2x_nic_unload(bp, UNLOAD_NORMAL); + rc = bnx2x_nic_load(bp, LOAD_NORMAL); } - return 0; + return rc; } static void bnx2x_get_pauseparam(struct net_device *dev, @@ -6301,6 +7242,9 @@ static int bnx2x_set_pauseparam(struct net_device *dev, { struct bnx2x *bp = netdev_priv(dev); + if (IS_E1HMF(bp)) + return 0; + DP(NETIF_MSG_LINK, "ethtool_pauseparam: cmd %d\n" DP_LEVEL " autoneg %d rx_pause %d tx_pause %d\n", epause->cmd, epause->autoneg, epause->rx_pause, epause->tx_pause); @@ -6317,7 +7261,7 @@ static int bnx2x_set_pauseparam(struct net_device *dev, bp->link_params.req_flow_ctrl = FLOW_CTRL_NONE; if (epause->autoneg) { - if (!(bp->supported & SUPPORTED_Autoneg)) { + if (!(bp->port.supported & SUPPORTED_Autoneg)) { DP(NETIF_MSG_LINK, "Autoneg not supported\n"); return -EINVAL; } @@ -6328,8 +7272,11 @@ static int bnx2x_set_pauseparam(struct net_device *dev, DP(NETIF_MSG_LINK, "req_flow_ctrl 0x%x\n", bp->link_params.req_flow_ctrl); - bnx2x_stop_stats(bp); - bnx2x_link_set(bp); + + if (netif_running(dev)) { + bnx2x_stop_stats(bp); + bnx2x_link_set(bp); + } return 0; } @@ -6531,18 +7478,25 @@ static void bnx2x_get_ethtool_stats(struct net_device *dev, static int bnx2x_phys_id(struct net_device *dev, u32 data) { struct bnx2x *bp = netdev_priv(dev); + int port = BP_PORT(bp); int i; + if (!netif_running(dev)) + return 0; + + if (!bp->port.pmf) + return 0; + if (data == 0) data = 2; for (i = 0; i < (data * 2); i++) { if ((i % 2) == 0) - bnx2x_set_led(bp, bp->port, LED_MODE_OPER, SPEED_1000, + bnx2x_set_led(bp, port, LED_MODE_OPER, SPEED_1000, bp->link_params.hw_led_mode, bp->link_params.chip_id); else - bnx2x_set_led(bp, bp->port, LED_MODE_OFF, 0, + bnx2x_set_led(bp, port, LED_MODE_OFF, 0, bp->link_params.hw_led_mode, bp->link_params.chip_id); @@ -6552,7 +7506,7 @@ static int bnx2x_phys_id(struct net_device *dev, u32 data) } if (bp->link_vars.link_up) - bnx2x_set_led(bp, bp->port, LED_MODE_OPER, + bnx2x_set_led(bp, port, LED_MODE_OPER, bp->link_vars.line_speed, bp->link_params.hw_led_mode, bp->link_params.chip_id); @@ -6609,117 +7563,40 @@ static int bnx2x_set_power_state(struct bnx2x *bp, pci_power_t state) switch (state) { case PCI_D0: - pci_write_config_word(bp->pdev, - bp->pm_cap + PCI_PM_CTRL, + pci_write_config_word(bp->pdev, bp->pm_cap + PCI_PM_CTRL, ((pmcsr & ~PCI_PM_CTRL_STATE_MASK) | PCI_PM_CTRL_PME_STATUS)); if (pmcsr & PCI_PM_CTRL_STATE_MASK) /* delay required during transition out of D3hot */ msleep(20); - break; - - case PCI_D3hot: - pmcsr &= ~PCI_PM_CTRL_STATE_MASK; - pmcsr |= 3; - - if (bp->wol) - pmcsr |= PCI_PM_CTRL_PME_ENABLE; - - pci_write_config_word(bp->pdev, bp->pm_cap + PCI_PM_CTRL, - pmcsr); - - /* No more memory access after this point until - * device is brought back to D0. - */ - break; - - default: - return -EINVAL; - } - return 0; -} - -/* - * net_device service functions - */ - -/* called with netif_tx_lock from set_multicast */ -static void bnx2x_set_rx_mode(struct net_device *dev) -{ - struct bnx2x *bp = netdev_priv(dev); - u32 rx_mode = BNX2X_RX_MODE_NORMAL; - - DP(NETIF_MSG_IFUP, "called dev->flags = %x\n", dev->flags); - - if (dev->flags & IFF_PROMISC) - rx_mode = BNX2X_RX_MODE_PROMISC; - - else if ((dev->flags & IFF_ALLMULTI) || - (dev->mc_count > BNX2X_MAX_MULTICAST)) - rx_mode = BNX2X_RX_MODE_ALLMULTI; - - else { /* some multicasts */ - int i, old, offset; - struct dev_mc_list *mclist; - struct mac_configuration_cmd *config = - bnx2x_sp(bp, mcast_config); - - for (i = 0, mclist = dev->mc_list; - mclist && (i < dev->mc_count); - i++, mclist = mclist->next) { - - config->config_table[i].cam_entry.msb_mac_addr = - swab16(*(u16 *)&mclist->dmi_addr[0]); - config->config_table[i].cam_entry.middle_mac_addr = - swab16(*(u16 *)&mclist->dmi_addr[2]); - config->config_table[i].cam_entry.lsb_mac_addr = - swab16(*(u16 *)&mclist->dmi_addr[4]); - config->config_table[i].cam_entry.flags = - cpu_to_le16(bp->port); - config->config_table[i].target_table_entry.flags = 0; - config->config_table[i].target_table_entry. - client_id = 0; - config->config_table[i].target_table_entry. - vlan_id = 0; - - DP(NETIF_MSG_IFUP, - "setting MCAST[%d] (%04x:%04x:%04x)\n", - i, config->config_table[i].cam_entry.msb_mac_addr, - config->config_table[i].cam_entry.middle_mac_addr, - config->config_table[i].cam_entry.lsb_mac_addr); - } - old = config->hdr.length_6b; - if (old > i) { - for (; i < old; i++) { - if (CAM_IS_INVALID(config->config_table[i])) { - i--; /* already invalidated */ - break; - } - /* invalidate */ - CAM_INVALIDATE(config->config_table[i]); - } - } + break; - if (CHIP_REV_IS_SLOW(bp)) - offset = BNX2X_MAX_EMUL_MULTI*(1 + bp->port); - else - offset = BNX2X_MAX_MULTICAST*(1 + bp->port); + case PCI_D3hot: + pmcsr &= ~PCI_PM_CTRL_STATE_MASK; + pmcsr |= 3; - config->hdr.length_6b = i; - config->hdr.offset = offset; - config->hdr.reserved0 = 0; - config->hdr.reserved1 = 0; + if (bp->wol) + pmcsr |= PCI_PM_CTRL_PME_ENABLE; - bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_SET_MAC, 0, - U64_HI(bnx2x_sp_mapping(bp, mcast_config)), - U64_LO(bnx2x_sp_mapping(bp, mcast_config)), 0); - } + pci_write_config_word(bp->pdev, bp->pm_cap + PCI_PM_CTRL, + pmcsr); - bp->rx_mode = rx_mode; - bnx2x_set_storm_rx_mode(bp); + /* No more memory access after this point until + * device is brought back to D0. + */ + break; + + default: + return -EINVAL; + } + return 0; } +/* + * net_device service functions + */ + static int bnx2x_poll(struct napi_struct *napi, int budget) { struct bnx2x_fastpath *fp = container_of(napi, struct bnx2x_fastpath, @@ -6729,7 +7606,7 @@ static int bnx2x_poll(struct napi_struct *napi, int budget) #ifdef BNX2X_STOP_ON_ERROR if (unlikely(bp->panic)) - goto out_panic; + goto poll_panic; #endif prefetch(fp->tx_buf_ring[TX_BD(fp->tx_pkt_cons)].skb); @@ -6738,30 +7615,28 @@ static int bnx2x_poll(struct napi_struct *napi, int budget) bnx2x_update_fpsb_idx(fp); - if (le16_to_cpu(*fp->tx_cons_sb) != fp->tx_pkt_cons) + if ((fp->tx_pkt_prod != le16_to_cpu(*fp->tx_cons_sb)) || + (fp->tx_pkt_prod != fp->tx_pkt_cons)) bnx2x_tx_int(fp, budget); - if (le16_to_cpu(*fp->rx_cons_sb) != fp->rx_comp_cons) work_done = bnx2x_rx_int(fp, budget); - rmb(); /* bnx2x_has_work() reads the status block */ /* must not complete if we consumed full budget */ if ((work_done < budget) && !bnx2x_has_work(fp)) { #ifdef BNX2X_STOP_ON_ERROR -out_panic: +poll_panic: #endif netif_rx_complete(bp->dev, napi); - bnx2x_ack_sb(bp, fp->index, USTORM_ID, + bnx2x_ack_sb(bp, FP_SB_ID(fp), USTORM_ID, le16_to_cpu(fp->fp_u_idx), IGU_INT_NOP, 1); - bnx2x_ack_sb(bp, fp->index, CSTORM_ID, + bnx2x_ack_sb(bp, FP_SB_ID(fp), CSTORM_ID, le16_to_cpu(fp->fp_c_idx), IGU_INT_ENABLE, 1); } - return work_done; } @@ -7055,18 +7930,145 @@ static int bnx2x_close(struct net_device *dev) return 0; } -/* Called with rtnl_lock */ +/* called with netif_tx_lock from set_multicast */ +static void bnx2x_set_rx_mode(struct net_device *dev) +{ + struct bnx2x *bp = netdev_priv(dev); + u32 rx_mode = BNX2X_RX_MODE_NORMAL; + int port = BP_PORT(bp); + + if (bp->state != BNX2X_STATE_OPEN) { + DP(NETIF_MSG_IFUP, "state is %x, returning\n", bp->state); + return; + } + + DP(NETIF_MSG_IFUP, "dev->flags = %x\n", dev->flags); + + if (dev->flags & IFF_PROMISC) + rx_mode = BNX2X_RX_MODE_PROMISC; + + else if ((dev->flags & IFF_ALLMULTI) || + ((dev->mc_count > BNX2X_MAX_MULTICAST) && CHIP_IS_E1(bp))) + rx_mode = BNX2X_RX_MODE_ALLMULTI; + + else { /* some multicasts */ + if (CHIP_IS_E1(bp)) { + int i, old, offset; + struct dev_mc_list *mclist; + struct mac_configuration_cmd *config = + bnx2x_sp(bp, mcast_config); + + for (i = 0, mclist = dev->mc_list; + mclist && (i < dev->mc_count); + i++, mclist = mclist->next) { + + config->config_table[i]. + cam_entry.msb_mac_addr = + swab16(*(u16 *)&mclist->dmi_addr[0]); + config->config_table[i]. + cam_entry.middle_mac_addr = + swab16(*(u16 *)&mclist->dmi_addr[2]); + config->config_table[i]. + cam_entry.lsb_mac_addr = + swab16(*(u16 *)&mclist->dmi_addr[4]); + config->config_table[i].cam_entry.flags = + cpu_to_le16(port); + config->config_table[i]. + target_table_entry.flags = 0; + config->config_table[i]. + target_table_entry.client_id = 0; + config->config_table[i]. + target_table_entry.vlan_id = 0; + + DP(NETIF_MSG_IFUP, + "setting MCAST[%d] (%04x:%04x:%04x)\n", i, + config->config_table[i]. + cam_entry.msb_mac_addr, + config->config_table[i]. + cam_entry.middle_mac_addr, + config->config_table[i]. + cam_entry.lsb_mac_addr); + } + old = config->hdr.length_6b; + if (old > i) { + for (; i < old; i++) { + if (CAM_IS_INVALID(config-> + config_table[i])) { + i--; /* already invalidated */ + break; + } + /* invalidate */ + CAM_INVALIDATE(config-> + config_table[i]); + } + } + + if (CHIP_REV_IS_SLOW(bp)) + offset = BNX2X_MAX_EMUL_MULTI*(1 + port); + else + offset = BNX2X_MAX_MULTICAST*(1 + port); + + config->hdr.length_6b = i; + config->hdr.offset = offset; + config->hdr.client_id = BP_CL_ID(bp); + config->hdr.reserved1 = 0; + + bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_SET_MAC, 0, + U64_HI(bnx2x_sp_mapping(bp, mcast_config)), + U64_LO(bnx2x_sp_mapping(bp, mcast_config)), + 0); + } else { /* E1H */ + /* Accept one or more multicasts */ + struct dev_mc_list *mclist; + u32 mc_filter[MC_HASH_SIZE]; + u32 crc, bit, regidx; + int i; + + memset(mc_filter, 0, 4 * MC_HASH_SIZE); + + for (i = 0, mclist = dev->mc_list; + mclist && (i < dev->mc_count); + i++, mclist = mclist->next) { + + DP(NETIF_MSG_IFUP, "Adding mcast MAC: " + "%02x:%02x:%02x:%02x:%02x:%02x\n", + mclist->dmi_addr[0], mclist->dmi_addr[1], + mclist->dmi_addr[2], mclist->dmi_addr[3], + mclist->dmi_addr[4], mclist->dmi_addr[5]); + + crc = crc32c_le(0, mclist->dmi_addr, ETH_ALEN); + bit = (crc >> 24) & 0xff; + regidx = bit >> 5; + bit &= 0x1f; + mc_filter[regidx] |= (1 << bit); + } + + for (i = 0; i < MC_HASH_SIZE; i++) + REG_WR(bp, MC_HASH_OFFSET(bp, i), + mc_filter[i]); + } + } + + bp->rx_mode = rx_mode; + bnx2x_set_storm_rx_mode(bp); +} + +/* called with rtnl_lock */ static int bnx2x_change_mac_addr(struct net_device *dev, void *p) { struct sockaddr *addr = p; struct bnx2x *bp = netdev_priv(dev); - if (!is_valid_ether_addr(addr->sa_data)) + if (!is_valid_ether_addr((u8 *)(addr->sa_data))) return -EINVAL; memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); - if (netif_running(dev)) - bnx2x_set_mac_addr(bp); + if (netif_running(dev)) { + if (CHIP_IS_E1(bp)) + bnx2x_set_mac_addr_e1(bp); + else + bnx2x_set_mac_addr_e1h(bp); + } return 0; } @@ -7080,7 +8082,7 @@ static int bnx2x_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) switch (cmd) { case SIOCGMIIPHY: - data->phy_id = bp->phy_addr; + data->phy_id = bp->port.phy_addr; /* fallthrough */ @@ -7090,12 +8092,12 @@ static int bnx2x_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) if (!netif_running(dev)) return -EAGAIN; - mutex_lock(&bp->phy_mutex); - err = bnx2x_cl45_read(bp, bp->port, 0, bp->phy_addr, + mutex_lock(&bp->port.phy_mutex); + err = bnx2x_cl45_read(bp, BP_PORT(bp), 0, bp->port.phy_addr, DEFAULT_PHY_DEV_ADDR, (data->reg_num & 0x1f), &mii_regval); data->val_out = mii_regval; - mutex_unlock(&bp->phy_mutex); + mutex_unlock(&bp->port.phy_mutex); return err; } @@ -7106,11 +8108,11 @@ static int bnx2x_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) if (!netif_running(dev)) return -EAGAIN; - mutex_lock(&bp->phy_mutex); - err = bnx2x_cl45_write(bp, bp->port, 0, bp->phy_addr, + mutex_lock(&bp->port.phy_mutex); + err = bnx2x_cl45_write(bp, BP_PORT(bp), 0, bp->port.phy_addr, DEFAULT_PHY_DEV_ADDR, (data->reg_num & 0x1f), data->val_in); - mutex_unlock(&bp->phy_mutex); + mutex_unlock(&bp->port.phy_mutex); return err; default: @@ -7121,10 +8123,11 @@ static int bnx2x_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) return -EOPNOTSUPP; } -/* Called with rtnl_lock */ +/* called with rtnl_lock */ static int bnx2x_change_mtu(struct net_device *dev, int new_mtu) { struct bnx2x *bp = netdev_priv(dev); + int rc = 0; if ((new_mtu > ETH_MAX_JUMBO_PACKET_SIZE) || ((new_mtu + ETH_HLEN) < ETH_MIN_PACKET_SIZE)) @@ -7137,10 +8140,11 @@ static int bnx2x_change_mtu(struct net_device *dev, int new_mtu) dev->mtu = new_mtu; if (netif_running(dev)) { - bnx2x_nic_unload(bp, 0); - bnx2x_nic_load(bp, 0); + bnx2x_nic_unload(bp, UNLOAD_NORMAL); + rc = bnx2x_nic_load(bp, LOAD_NORMAL); } - return 0; + + return rc; } static void bnx2x_tx_timeout(struct net_device *dev) @@ -7156,7 +8160,7 @@ static void bnx2x_tx_timeout(struct net_device *dev) } #ifdef BCM_VLAN -/* Called with rtnl_lock */ +/* called with rtnl_lock */ static void bnx2x_vlan_rx_register(struct net_device *dev, struct vlan_group *vlgrp) { @@ -7166,6 +8170,7 @@ static void bnx2x_vlan_rx_register(struct net_device *dev, if (netif_running(dev)) bnx2x_set_client_config(bp); } + #endif #if defined(HAVE_POLL_CONTROLLER) || defined(CONFIG_NET_POLL_CONTROLLER) @@ -7179,36 +8184,8 @@ static void poll_bnx2x(struct net_device *dev) } #endif -static void bnx2x_reset_task(struct work_struct *work) -{ - struct bnx2x *bp = container_of(work, struct bnx2x, reset_task); - -#ifdef BNX2X_STOP_ON_ERROR - BNX2X_ERR("reset task called but STOP_ON_ERROR defined" - " so reset not done to allow debug dump,\n" - KERN_ERR " you will need to reboot when done\n"); - return; -#endif - - if (!netif_running(bp->dev)) - return; - - rtnl_lock(); - - if (bp->state != BNX2X_STATE_OPEN) { - DP(NETIF_MSG_TX_ERR, "state is %x, returning\n", bp->state); - goto reset_task_exit; - } - - bnx2x_nic_unload(bp, 0); - bnx2x_nic_load(bp, 0); - -reset_task_exit: - rtnl_unlock(); -} - -static int __devinit bnx2x_init_board(struct pci_dev *pdev, - struct net_device *dev) +static int __devinit bnx2x_init_dev(struct pci_dev *pdev, + struct net_device *dev) { struct bnx2x *bp; int rc; @@ -7216,8 +8193,10 @@ static int __devinit bnx2x_init_board(struct pci_dev *pdev, SET_NETDEV_DEV(dev, &pdev->dev); bp = netdev_priv(dev); + bp->dev = dev; + bp->pdev = pdev; bp->flags = 0; - bp->port = PCI_FUNC(pdev->devfn); + bp->func = PCI_FUNC(pdev->devfn); rc = pci_enable_device(pdev); if (rc) { @@ -7239,14 +8218,17 @@ static int __devinit bnx2x_init_board(struct pci_dev *pdev, goto err_out_disable; } - rc = pci_request_regions(pdev, DRV_MODULE_NAME); - if (rc) { - printk(KERN_ERR PFX "Cannot obtain PCI resources," - " aborting\n"); - goto err_out_disable; - } + if (atomic_read(&pdev->enable_cnt) == 1) { + rc = pci_request_regions(pdev, DRV_MODULE_NAME); + if (rc) { + printk(KERN_ERR PFX "Cannot obtain PCI resources," + " aborting\n"); + goto err_out_disable; + } - pci_set_master(pdev); + pci_set_master(pdev); + pci_save_state(pdev); + } bp->pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM); if (bp->pm_cap == 0) { @@ -7280,13 +8262,9 @@ static int __devinit bnx2x_init_board(struct pci_dev *pdev, goto err_out_release; } - bp->dev = dev; - bp->pdev = pdev; - - INIT_WORK(&bp->reset_task, bnx2x_reset_task); - INIT_WORK(&bp->sp_task, bnx2x_sp_task); - - dev->base_addr = pci_resource_start(pdev, 0); + dev->mem_start = pci_resource_start(pdev, 0); + dev->base_addr = dev->mem_start; + dev->mem_end = pci_resource_end(pdev, 0); dev->irq = pdev->irq; @@ -7298,8 +8276,9 @@ static int __devinit bnx2x_init_board(struct pci_dev *pdev, goto err_out_release; } - bp->doorbells = ioremap_nocache(pci_resource_start(pdev , 2), - pci_resource_len(pdev, 2)); + bp->doorbells = ioremap_nocache(pci_resource_start(pdev, 2), + min_t(u64, BNX2X_DB_SIZE, + pci_resource_len(pdev, 2))); if (!bp->doorbells) { printk(KERN_ERR PFX "Cannot map doorbell space, aborting\n"); rc = -ENOMEM; @@ -7308,47 +8287,43 @@ static int __devinit bnx2x_init_board(struct pci_dev *pdev, bnx2x_set_power_state(bp, PCI_D0); - bnx2x_get_hwinfo(bp); - - - if (nomcp) { - printk(KERN_ERR PFX "MCP disabled, will only" - " init first device\n"); - onefunc = 1; - } - - if (onefunc && bp->port) { - printk(KERN_ERR PFX "Second device disabled, exiting\n"); - rc = -ENODEV; - goto err_out_unmap; - } - - bp->tx_ring_size = MAX_TX_AVAIL; - bp->rx_ring_size = MAX_RX_AVAIL; - - bp->rx_csum = 1; - - bp->rx_offset = 0; - - bp->tx_quick_cons_trip_int = 0xff; - bp->tx_quick_cons_trip = 0xff; - bp->tx_ticks_int = 50; - bp->tx_ticks = 50; + /* clean indirect addresses */ + pci_write_config_dword(bp->pdev, PCICFG_GRC_ADDRESS, + PCICFG_VENDOR_ID_OFFSET); + REG_WR(bp, PXP2_REG_PGL_ADDR_88_F0 + BP_PORT(bp)*16, 0); + REG_WR(bp, PXP2_REG_PGL_ADDR_8C_F0 + BP_PORT(bp)*16, 0); + REG_WR(bp, PXP2_REG_PGL_ADDR_90_F0 + BP_PORT(bp)*16, 0); + REG_WR(bp, PXP2_REG_PGL_ADDR_94_F0 + BP_PORT(bp)*16, 0); - bp->rx_quick_cons_trip_int = 0xff; - bp->rx_quick_cons_trip = 0xff; - bp->rx_ticks_int = 25; - bp->rx_ticks = 25; + dev->hard_start_xmit = bnx2x_start_xmit; + dev->watchdog_timeo = TX_TIMEOUT; - bp->stats_ticks = 1000000 & 0xffff00; + dev->ethtool_ops = &bnx2x_ethtool_ops; + dev->open = bnx2x_open; + dev->stop = bnx2x_close; + dev->set_multicast_list = bnx2x_set_rx_mode; + dev->set_mac_address = bnx2x_change_mac_addr; + dev->do_ioctl = bnx2x_ioctl; + dev->change_mtu = bnx2x_change_mtu; + dev->tx_timeout = bnx2x_tx_timeout; +#ifdef BCM_VLAN + dev->vlan_rx_register = bnx2x_vlan_rx_register; +#endif +#if defined(HAVE_POLL_CONTROLLER) || defined(CONFIG_NET_POLL_CONTROLLER) + dev->poll_controller = poll_bnx2x; +#endif + dev->features |= NETIF_F_SG; + dev->features |= NETIF_F_HW_CSUM; + if (bp->flags & USING_DAC_FLAG) + dev->features |= NETIF_F_HIGHDMA; +#ifdef BCM_VLAN + dev->features |= (NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX); +#endif + dev->features |= (NETIF_F_TSO | NETIF_F_TSO_ECN); bp->timer_interval = HZ; bp->current_interval = (poll ? poll : HZ); - init_timer(&bp->timer); - bp->timer.expires = jiffies + bp->current_interval; - bp->timer.data = (unsigned long) bp; - bp->timer.function = bnx2x_timer; return 0; @@ -7357,14 +8332,14 @@ err_out_unmap: iounmap(bp->regview); bp->regview = NULL; } - if (bp->doorbells) { iounmap(bp->doorbells); bp->doorbells = NULL; } err_out_release: - pci_release_regions(pdev); + if (atomic_read(&pdev->enable_cnt) == 1) + pci_release_regions(pdev); err_out_disable: pci_disable_device(pdev); @@ -7398,7 +8373,6 @@ static int __devinit bnx2x_init_one(struct pci_dev *pdev, struct net_device *dev = NULL; struct bnx2x *bp; int rc; - int port = PCI_FUNC(pdev->devfn); DECLARE_MAC_BUF(mac); if (version_printed++ == 0) @@ -7406,78 +8380,62 @@ static int __devinit bnx2x_init_one(struct pci_dev *pdev, /* dev zeroed in init_etherdev */ dev = alloc_etherdev(sizeof(*bp)); - if (!dev) + if (!dev) { + printk(KERN_ERR PFX "Cannot allocate net device\n"); return -ENOMEM; + } netif_carrier_off(dev); bp = netdev_priv(dev); bp->msglevel = debug; - if (port && onefunc) { - printk(KERN_ERR PFX "second function disabled. exiting\n"); - free_netdev(dev); - return 0; - } - - rc = bnx2x_init_board(pdev, dev); + rc = bnx2x_init_dev(pdev, dev); if (rc < 0) { free_netdev(dev); return rc; } - dev->hard_start_xmit = bnx2x_start_xmit; - dev->watchdog_timeo = TX_TIMEOUT; - - dev->ethtool_ops = &bnx2x_ethtool_ops; - dev->open = bnx2x_open; - dev->stop = bnx2x_close; - dev->set_multicast_list = bnx2x_set_rx_mode; - dev->set_mac_address = bnx2x_change_mac_addr; - dev->do_ioctl = bnx2x_ioctl; - dev->change_mtu = bnx2x_change_mtu; - dev->tx_timeout = bnx2x_tx_timeout; -#ifdef BCM_VLAN - dev->vlan_rx_register = bnx2x_vlan_rx_register; -#endif -#if defined(HAVE_POLL_CONTROLLER) || defined(CONFIG_NET_POLL_CONTROLLER) - dev->poll_controller = poll_bnx2x; -#endif - dev->features |= NETIF_F_SG; - if (bp->flags & USING_DAC_FLAG) - dev->features |= NETIF_F_HIGHDMA; - dev->features |= NETIF_F_IP_CSUM; -#ifdef BCM_VLAN - dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; -#endif - dev->features |= NETIF_F_TSO | NETIF_F_TSO_ECN; - rc = register_netdev(dev); if (rc) { dev_err(&pdev->dev, "Cannot register net device\n"); - if (bp->regview) - iounmap(bp->regview); - if (bp->doorbells) - iounmap(bp->doorbells); - pci_release_regions(pdev); - pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); - free_netdev(dev); - return rc; + goto init_one_exit; } pci_set_drvdata(pdev, dev); - bp->name = board_info[ent->driver_data].name; + rc = bnx2x_init_bp(bp); + if (rc) { + unregister_netdev(dev); + goto init_one_exit; + } + + bp->common.name = board_info[ent->driver_data].name; printk(KERN_INFO "%s: %s (%c%d) PCI-E x%d %s found at mem %lx," - " IRQ %d, ", dev->name, bp->name, - ((CHIP_ID(bp) & 0xf000) >> 12) + 'A', - ((CHIP_ID(bp) & 0x0ff0) >> 4), + " IRQ %d, ", dev->name, bp->common.name, + (CHIP_REV(bp) >> 12) + 'A', (CHIP_METAL(bp) >> 4), bnx2x_get_pcie_width(bp), (bnx2x_get_pcie_speed(bp) == 2) ? "5GHz (Gen2)" : "2.5GHz", dev->base_addr, bp->pdev->irq); printk(KERN_CONT "node addr %s\n", print_mac(mac, dev->dev_addr)); return 0; + +init_one_exit: + if (bp->regview) + iounmap(bp->regview); + + if (bp->doorbells) + iounmap(bp->doorbells); + + free_netdev(dev); + + if (atomic_read(&pdev->enable_cnt) == 1) + pci_release_regions(pdev); + + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + + return rc; } static void __devexit bnx2x_remove_one(struct pci_dev *pdev) @@ -7486,11 +8444,9 @@ static void __devexit bnx2x_remove_one(struct pci_dev *pdev) struct bnx2x *bp; if (!dev) { - /* we get here if init_one() fails */ printk(KERN_ERR PFX "BAD net device from bnx2x_init_one\n"); return; } - bp = netdev_priv(dev); unregister_netdev(dev); @@ -7502,7 +8458,10 @@ static void __devexit bnx2x_remove_one(struct pci_dev *pdev) iounmap(bp->doorbells); free_netdev(dev); - pci_release_regions(pdev); + + if (atomic_read(&pdev->enable_cnt) == 1) + pci_release_regions(pdev); + pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); } @@ -7512,21 +8471,29 @@ static int bnx2x_suspend(struct pci_dev *pdev, pm_message_t state) struct net_device *dev = pci_get_drvdata(pdev); struct bnx2x *bp; - if (!dev) - return 0; + if (!dev) { + printk(KERN_ERR PFX "BAD net device from bnx2x_init_one\n"); + return -ENODEV; + } + bp = netdev_priv(dev); - if (!netif_running(dev)) - return 0; + rtnl_lock(); - bp = netdev_priv(dev); + pci_save_state(pdev); - bnx2x_nic_unload(bp, 0); + if (!netif_running(dev)) { + rtnl_unlock(); + return 0; + } netif_device_detach(dev); - pci_save_state(pdev); + bnx2x_nic_unload(bp, UNLOAD_NORMAL); + bnx2x_set_power_state(bp, pci_choose_state(pdev, state)); + rtnl_unlock(); + return 0; } @@ -7540,21 +8507,25 @@ static int bnx2x_resume(struct pci_dev *pdev) printk(KERN_ERR PFX "BAD net device from bnx2x_init_one\n"); return -ENODEV; } - - if (!netif_running(dev)) - return 0; - bp = netdev_priv(dev); + rtnl_lock(); + pci_restore_state(pdev); + + if (!netif_running(dev)) { + rtnl_unlock(); + return 0; + } + bnx2x_set_power_state(bp, PCI_D0); netif_device_attach(dev); - rc = bnx2x_nic_load(bp, 0); - if (rc) - return rc; + rc = bnx2x_nic_load(bp, LOAD_NORMAL); - return 0; + rtnl_unlock(); + + return rc; } static struct pci_driver bnx2x_pci_driver = { diff --git a/drivers/net/bnx2x_reg.h b/drivers/net/bnx2x_reg.h index 8707c0d05d9a..15c9a9946724 100644 --- a/drivers/net/bnx2x_reg.h +++ b/drivers/net/bnx2x_reg.h @@ -38,21 +38,19 @@ was asserted. */ #define BRB1_REG_NUM_OF_FULL_CYCLES_0 0x600c8 #define BRB1_REG_NUM_OF_FULL_CYCLES_1 0x600cc -#define BRB1_REG_NUM_OF_FULL_CYCLES_2 0x600d0 -#define BRB1_REG_NUM_OF_FULL_CYCLES_3 0x600d4 #define BRB1_REG_NUM_OF_FULL_CYCLES_4 0x600d8 /* [ST 32] The number of cycles that the pause signal towards MAC #0 was asserted. */ #define BRB1_REG_NUM_OF_PAUSE_CYCLES_0 0x600b8 #define BRB1_REG_NUM_OF_PAUSE_CYCLES_1 0x600bc -#define BRB1_REG_NUM_OF_PAUSE_CYCLES_2 0x600c0 -#define BRB1_REG_NUM_OF_PAUSE_CYCLES_3 0x600c4 /* [RW 10] Write client 0: De-assert pause threshold. */ #define BRB1_REG_PAUSE_HIGH_THRESHOLD_0 0x60078 #define BRB1_REG_PAUSE_HIGH_THRESHOLD_1 0x6007c /* [RW 10] Write client 0: Assert pause threshold. */ #define BRB1_REG_PAUSE_LOW_THRESHOLD_0 0x60068 #define BRB1_REG_PAUSE_LOW_THRESHOLD_1 0x6006c +/* [R 24] The number of full blocks occpied by port. */ +#define BRB1_REG_PORT_NUM_OCC_BLOCKS_0 0x60094 /* [RW 1] Reset the design by software. */ #define BRB1_REG_SOFT_RESET 0x600dc /* [R 5] Used to read the value of the XX protection CAM occupancy counter. */ @@ -513,7 +511,6 @@ /* [RW 15] Interrupt table Read and write access to it is not possible in the middle of the work */ #define CSEM_REG_INT_TABLE 0x200400 -#define CSEM_REG_INT_TABLE_SIZE 256 /* [ST 24] Statistics register. The number of messages that entered through FIC0 */ #define CSEM_REG_MSG_NUM_FIC0 0x200000 @@ -587,13 +584,10 @@ #define DBG_REG_DBG_PRTY_MASK 0xc0a8 /* [R 1] Parity register #0 read */ #define DBG_REG_DBG_PRTY_STS 0xc09c -/* [RW 2] debug only: These bits indicate the credit for PCI request type 4 - interface; MUST be configured AFTER pci_ext_buffer_strt_addr_lsb/msb are - configured */ -#define DBG_REG_PCI_REQ_CREDIT 0xc120 /* [RW 32] Commands memory. The address to command X; row Y is to calculated as 14*X+Y. */ #define DMAE_REG_CMD_MEM 0x102400 +#define DMAE_REG_CMD_MEM_SIZE 224 /* [RW 1] If 0 - the CRC-16c initial value is all zeroes; if 1 - the CRC-16c initial value is all ones. */ #define DMAE_REG_CRC16C_INIT 0x10201c @@ -1626,7 +1620,7 @@ is reset to 0x080; giving a default blink period of approximately 8Hz. */ #define NIG_REG_LED_CONTROL_BLINK_RATE_P0 0x10310 /* [RW 1] Port0: If set along with the - nig_registers_led_control_override_traffic_p0.led_control_override_traffic_p0 + ~nig_registers_led_control_override_traffic_p0.led_control_override_traffic_p0 bit and ~nig_registers_led_control_traffic_p0.led_control_traffic_p0 LED bit; the Traffic LED will blink with the blink rate specified in ~nig_registers_led_control_blink_rate_p0.led_control_blink_rate_p0 and @@ -1733,9 +1727,21 @@ /* [R 32] Rx statistics : In user packets discarded due to BRB backpressure for port0 */ #define NIG_REG_STAT0_BRB_DISCARD 0x105f0 +/* [WB_R 36] Tx statistics : Number of packets from emac0 or bmac0 that + between 1024 and 1522 bytes for port0 */ +#define NIG_REG_STAT0_EGRESS_MAC_PKT0 0x10750 +/* [WB_R 36] Tx statistics : Number of packets from emac0 or bmac0 that + between 1523 bytes and above for port0 */ +#define NIG_REG_STAT0_EGRESS_MAC_PKT1 0x10760 /* [R 32] Rx statistics : In user packets discarded due to BRB backpressure for port1 */ #define NIG_REG_STAT1_BRB_DISCARD 0x10628 +/* [WB_R 36] Tx statistics : Number of packets from emac1 or bmac1 that + between 1024 and 1522 bytes for port1 */ +#define NIG_REG_STAT1_EGRESS_MAC_PKT0 0x107a0 +/* [WB_R 36] Tx statistics : Number of packets from emac1 or bmac1 that + between 1523 bytes and above for port1 */ +#define NIG_REG_STAT1_EGRESS_MAC_PKT1 0x107b0 /* [WB_R 64] Rx statistics : User octets received for LP */ #define NIG_REG_STAT2_BRB_OCTET 0x107e0 #define NIG_REG_STATUS_INTERRUPT_PORT0 0x10328 @@ -1849,7 +1855,6 @@ #define PRS_REG_CFC_SEARCH_INITIAL_CREDIT 0x4011c /* [RW 24] CID for port 0 if no match */ #define PRS_REG_CID_PORT_0 0x400fc -#define PRS_REG_CID_PORT_1 0x40100 /* [RW 32] The CM header for flush message where 'load existed' bit in CFC load response is reset and packet type is 0. Used in packet start message to TCM. */ @@ -1957,6 +1962,10 @@ #define PXP2_REG_HST_DATA_FIFO_STATUS 0x12047c /* [R 7] Debug only: Number of used entries in the header FIFO */ #define PXP2_REG_HST_HEADER_FIFO_STATUS 0x120478 +#define PXP2_REG_PGL_ADDR_88_F0 0x120534 +#define PXP2_REG_PGL_ADDR_8C_F0 0x120538 +#define PXP2_REG_PGL_ADDR_90_F0 0x12053c +#define PXP2_REG_PGL_ADDR_94_F0 0x120540 #define PXP2_REG_PGL_CONTROL0 0x120490 #define PXP2_REG_PGL_CONTROL1 0x120514 /* [RW 32] third dword data of expansion rom request. this register is @@ -2060,12 +2069,13 @@ #define PXP2_REG_PSWRQ_SRC0_L2P 0x120054 #define PXP2_REG_PSWRQ_TM0_L2P 0x12001c #define PXP2_REG_PSWRQ_TSDM0_L2P 0x1200e0 -/* [RW 25] Interrupt mask register #0 read/write */ -#define PXP2_REG_PXP2_INT_MASK 0x120578 -/* [R 25] Interrupt register #0 read */ -#define PXP2_REG_PXP2_INT_STS 0x12056c -/* [RC 25] Interrupt register #0 read clear */ -#define PXP2_REG_PXP2_INT_STS_CLR 0x120570 +/* [RW 32] Interrupt mask register #0 read/write */ +#define PXP2_REG_PXP2_INT_MASK_0 0x120578 +/* [R 32] Interrupt register #0 read */ +#define PXP2_REG_PXP2_INT_STS_0 0x12056c +#define PXP2_REG_PXP2_INT_STS_1 0x120608 +/* [RC 32] Interrupt register #0 read clear */ +#define PXP2_REG_PXP2_INT_STS_CLR_0 0x120570 /* [RW 32] Parity mask register #0 read/write */ #define PXP2_REG_PXP2_PRTY_MASK_0 0x120588 #define PXP2_REG_PXP2_PRTY_MASK_1 0x120598 @@ -2811,22 +2821,6 @@ #define QM_REG_QVOQIDX_97 0x16e490 #define QM_REG_QVOQIDX_98 0x16e494 #define QM_REG_QVOQIDX_99 0x16e498 -/* [R 24] Remaining pause timeout for queues 15-0 */ -#define QM_REG_REMAINPAUSETM0 0x168418 -/* [R 24] Remaining pause timeout for queues 31-16 */ -#define QM_REG_REMAINPAUSETM1 0x16841c -/* [R 24] Remaining pause timeout for queues 47-32 */ -#define QM_REG_REMAINPAUSETM2 0x16e69c -/* [R 24] Remaining pause timeout for queues 63-48 */ -#define QM_REG_REMAINPAUSETM3 0x16e6a0 -/* [R 24] Remaining pause timeout for queues 79-64 */ -#define QM_REG_REMAINPAUSETM4 0x16e6a4 -/* [R 24] Remaining pause timeout for queues 95-80 */ -#define QM_REG_REMAINPAUSETM5 0x16e6a8 -/* [R 24] Remaining pause timeout for queues 111-96 */ -#define QM_REG_REMAINPAUSETM6 0x16e6ac -/* [R 24] Remaining pause timeout for queues 127-112 */ -#define QM_REG_REMAINPAUSETM7 0x16e6b0 /* [RW 1] Initialization bit command */ #define QM_REG_SOFT_RESET 0x168428 /* [RW 8] The credit cost per every task in the QM. A value per each VOQ */ @@ -3826,7 +3820,6 @@ /* [RW 15] Interrupt table Read and write access to it is not possible in the middle of the work */ #define TSEM_REG_INT_TABLE 0x180400 -#define TSEM_REG_INT_TABLE_SIZE 256 /* [ST 24] Statistics register. The number of messages that entered through FIC0 */ #define TSEM_REG_MSG_NUM_FIC0 0x180000 @@ -4283,7 +4276,6 @@ /* [RW 15] Interrupt table Read and write access to it is not possible in the middle of the work */ #define USEM_REG_INT_TABLE 0x300400 -#define USEM_REG_INT_TABLE_SIZE 256 /* [ST 24] Statistics register. The number of messages that entered through FIC0 */ #define USEM_REG_MSG_NUM_FIC0 0x300000 @@ -4802,7 +4794,6 @@ /* [RW 15] Interrupt table Read and write access to it is not possible in the middle of the work */ #define XSEM_REG_INT_TABLE 0x280400 -#define XSEM_REG_INT_TABLE_SIZE 256 /* [ST 24] Statistics register. The number of messages that entered through FIC0 */ #define XSEM_REG_MSG_NUM_FIC0 0x280000 @@ -4930,10 +4921,7 @@ #define EMAC_MDIO_MODE_CLOCK_CNT (0x3fL<<16) #define EMAC_MDIO_MODE_CLOCK_CNT_BITSHIFT 16 #define EMAC_MODE_25G_MODE (1L<<5) -#define EMAC_MODE_ACPI_RCVD (1L<<20) #define EMAC_MODE_HALF_DUPLEX (1L<<1) -#define EMAC_MODE_MPKT (1L<<18) -#define EMAC_MODE_MPKT_RCVD (1L<<19) #define EMAC_MODE_PORT_GMII (2L<<2) #define EMAC_MODE_PORT_MII (1L<<2) #define EMAC_MODE_PORT_MII_10M (3L<<2) diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index caa000596b25..e74b14acf8e0 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1949,6 +1949,8 @@ #define PCI_DEVICE_ID_NX2_5708 0x164c #define PCI_DEVICE_ID_TIGON3_5702FE 0x164d #define PCI_DEVICE_ID_NX2_57710 0x164e +#define PCI_DEVICE_ID_NX2_57711 0x164f +#define PCI_DEVICE_ID_NX2_57711E 0x1650 #define PCI_DEVICE_ID_TIGON3_5705 0x1653 #define PCI_DEVICE_ID_TIGON3_5705_2 0x1654 #define PCI_DEVICE_ID_TIGON3_5720 0x1658 -- cgit v1.2.3 From f8dd0ecbb74d4b220b105d77c0633945ebb5453e Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 24 Jun 2008 22:16:04 +0100 Subject: DM9000: Allow the use of the NSR register to get link status. The DM9000's internal PHY reports a copy of the link status in the NSR register of the chip. Reading the status when polling for link status is faster as it eliminates the need to sleep, but does not print as much information. Add an platform flag to force this behaviour, and a Kconfig option to allow it to be forced to the faster method always. Signed-off-by: Ben Dooks Signed-off-by: Jeff Garzik --- drivers/net/Kconfig | 9 +++++++++ drivers/net/dm9000.c | 41 +++++++++++++++++++++++++++++++++++++++-- include/linux/dm9000.h | 1 + 3 files changed, 49 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 287d0873c60d..4d69474b6125 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -938,6 +938,15 @@ config DM9000 To compile this driver as a module, choose M here. The module will be called dm9000. +config DM9000_FORCE_SIMPLE_PHY_POLL + bool "Force simple NSR based PHY polling" + depends on DM9000 + ---help--- + This configuration forces the DM9000 to use the NSR's LinkStatus + bit to determine if the link is up or down instead of the more + costly MII PHY reads. Note, this will not work if the chip is + operating with an external PHY. + config ENC28J60 tristate "ENC28J60 support" depends on EXPERIMENTAL && SPI && NET_ETHERNET diff --git a/drivers/net/dm9000.c b/drivers/net/dm9000.c index 7c38f6129b55..5ad2ec537684 100644 --- a/drivers/net/dm9000.c +++ b/drivers/net/dm9000.c @@ -552,15 +552,48 @@ static const struct ethtool_ops dm9000_ethtool_ops = { .set_eeprom = dm9000_set_eeprom, }; +static void dm9000_show_carrier(board_info_t *db, + unsigned carrier, unsigned nsr) +{ + struct net_device *ndev = db->ndev; + unsigned ncr = dm9000_read_locked(db, DM9000_NCR); + + if (carrier) + dev_info(db->dev, "%s: link up, %dMbps, %s-duplex, no LPA\n", + ndev->name, (nsr & NSR_SPEED) ? 10 : 100, + (ncr & NCR_FDX) ? "full" : "half"); + else + dev_info(db->dev, "%s: link down\n", ndev->name); +} + static void dm9000_poll_work(struct work_struct *w) { struct delayed_work *dw = container_of(w, struct delayed_work, work); board_info_t *db = container_of(dw, board_info_t, phy_poll); + struct net_device *ndev = db->ndev; + + if (db->flags & DM9000_PLATF_SIMPLE_PHY && + !(db->flags & DM9000_PLATF_EXT_PHY)) { + unsigned nsr = dm9000_read_locked(db, DM9000_NSR); + unsigned old_carrier = netif_carrier_ok(ndev) ? 1 : 0; + unsigned new_carrier; - mii_check_media(&db->mii, netif_msg_link(db), 0); + new_carrier = (nsr & NSR_LINKST) ? 1 : 0; + + if (old_carrier != new_carrier) { + if (netif_msg_link(db)) + dm9000_show_carrier(db, new_carrier, nsr); + + if (!new_carrier) + netif_carrier_off(ndev); + else + netif_carrier_on(ndev); + } + } else + mii_check_media(&db->mii, netif_msg_link(db), 0); - if (netif_running(db->ndev)) + if (netif_running(ndev)) dm9000_schedule_poll(db); } @@ -1267,6 +1300,10 @@ dm9000_probe(struct platform_device *pdev) db->flags = pdata->flags; } +#ifdef CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL + db->flags |= DM9000_PLATF_SIMPLE_PHY; +#endif + dm9000_reset(db); /* try multiple times, DM9000 sometimes gets the read wrong */ diff --git a/include/linux/dm9000.h b/include/linux/dm9000.h index a3750462f9e3..fc82446b6425 100644 --- a/include/linux/dm9000.h +++ b/include/linux/dm9000.h @@ -21,6 +21,7 @@ #define DM9000_PLATF_32BITONLY (0x0004) #define DM9000_PLATF_EXT_PHY (0x0008) #define DM9000_PLATF_NO_EEPROM (0x0010) +#define DM9000_PLATF_SIMPLE_PHY (0x0020) /* Use NSR to find LinkStatus */ /* platfrom data for platfrom device structure's platfrom_data field */ -- cgit v1.2.3 From f3146aff7f283c8699e0c97df6307a705786eeba Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:22:56 -0300 Subject: rfkill: clarify meaning of rfkill states rfkill really should have been named rfswitch. As it is, one can get confused whether RFKILL_STATE_ON means the KILL switch is on (and therefore, the radio is being *blocked* from operating), or whether it means the RADIO rf output is on. Clearly state that RFKILL_STATE_ON means the radio is *unblocked* from operating (i.e. there is no rf killing going on). Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Cc: Dmitry Torokhov Signed-off-by: John W. Linville --- Documentation/rfkill.txt | 7 +++++++ include/linux/rfkill.h | 6 +++--- 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/Documentation/rfkill.txt b/Documentation/rfkill.txt index a83ff23cd68c..ec75d6d34785 100644 --- a/Documentation/rfkill.txt +++ b/Documentation/rfkill.txt @@ -8,6 +8,13 @@ rfkill - RF switch subsystem support =============================================================================== 1: Implementation details +The rfkill switch subsystem exists to add a generic interface to circuitry that +can enable or disable the RF output of a radio *transmitter* of any type. + +When a rfkill switch is in the RFKILL_STATE_ON, the radio transmitter is +*enabled*. When the rfkill switch is in the RFKILL_STATE_OFF, the radio +transmitter is *disabled*. + The rfkill switch subsystem offers support for keys often found on laptops to enable wireless devices like WiFi and Bluetooth. diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h index e3ab21d7fc7f..ca89ae1b0219 100644 --- a/include/linux/rfkill.h +++ b/include/linux/rfkill.h @@ -44,8 +44,8 @@ enum rfkill_type { }; enum rfkill_state { - RFKILL_STATE_OFF = 0, - RFKILL_STATE_ON = 1, + RFKILL_STATE_OFF = 0, /* Radio output blocked */ + RFKILL_STATE_ON = 1, /* Radio output active */ }; /** @@ -53,7 +53,7 @@ enum rfkill_state { * @name: Name of the switch. * @type: Radio type which the button controls, the value stored * here should be a value from enum rfkill_type. - * @state: State of the switch (on/off). + * @state: State of the switch, "ON" means radio can operate. * @user_claim_unsupported: Whether the hardware supports exclusive * RF-kill control by userspace. Set this before registering. * @user_claim: Set when the switch is controlled exlusively by userspace. -- cgit v1.2.3 From 801e49af4c1a9b988ba0d25de2b368c99c3bf2b3 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:23:00 -0300 Subject: rfkill: add read-write rfkill switch support Currently, rfkill support for read/write rfkill switches is hacked through a round-trip over the input layer and rfkill-input to let a driver sync rfkill->state to hardware changes. This is buggy and sub-optimal. It causes real problems. It is best to think of the rfkill class as supporting only write-only switches at the moment. In order to implement the read/write functionality properly: Add a get_state() hook that is called by the class every time it needs to fetch the current state of the switch. Add a call to this hook every time the *current* state of the radio plays a role in a decision. Also add a force_state() method that can be used to forcefully syncronize the class' idea of the current state of the switch. This allows for a faster implementation of the read/write functionality, as a driver which get events on switch changes can avoid the need for a get_state() hook. If the get_state() hook is left as NULL, current behaviour is maintained, so this change is fully backwards compatible with the current rfkill drivers. For hardware that issues events when the rfkill state changes, leave get_state() NULL in the rfkill struct, set the initial state properly before registering with the rfkill class, and use the force_state() method in the driver to keep the rfkill interface up-to-date. get_state() can be called by the class from atomic context. It must not sleep. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Cc: Dmitry Torokhov Signed-off-by: John W. Linville --- include/linux/rfkill.h | 5 +++++ net/rfkill/rfkill.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 55 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h index ca89ae1b0219..844e96114861 100644 --- a/include/linux/rfkill.h +++ b/include/linux/rfkill.h @@ -61,6 +61,8 @@ enum rfkill_state { * @data: Pointer to the RF button drivers private data which will be * passed along when toggling radio state. * @toggle_radio(): Mandatory handler to control state of the radio. + * @get_state(): handler to read current radio state from hardware, + * may be called from atomic context, should return 0 on success. * @led_trigger: A LED trigger for this button's LED. * @dev: Device structure integrating the switch into device tree. * @node: Used to place switch into list of all switches known to the @@ -80,6 +82,7 @@ struct rfkill { void *data; int (*toggle_radio)(void *data, enum rfkill_state state); + int (*get_state)(void *data, enum rfkill_state *state); #ifdef CONFIG_RFKILL_LEDS struct led_trigger led_trigger; @@ -95,6 +98,8 @@ void rfkill_free(struct rfkill *rfkill); int rfkill_register(struct rfkill *rfkill); void rfkill_unregister(struct rfkill *rfkill); +int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state); + /** * rfkill_get_led_name - Get the LED trigger name for the button's LED. * This function might return a NULL pointer if registering of the diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index 3edc585dcfa6..4ae4486c77ea 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -62,19 +62,39 @@ static void rfkill_led_trigger(struct rfkill *rfkill, #endif /* CONFIG_RFKILL_LEDS */ } +static void update_rfkill_state(struct rfkill *rfkill) +{ + enum rfkill_state newstate; + + if (rfkill->get_state) { + mutex_lock(&rfkill->mutex); + if (!rfkill->get_state(rfkill->data, &newstate)) + rfkill->state = newstate; + mutex_unlock(&rfkill->mutex); + } +} + static int rfkill_toggle_radio(struct rfkill *rfkill, enum rfkill_state state) { int retval = 0; + enum rfkill_state oldstate, newstate; + + oldstate = rfkill->state; + + if (rfkill->get_state && + !rfkill->get_state(rfkill->data, &newstate)) + rfkill->state = newstate; if (state != rfkill->state) { retval = rfkill->toggle_radio(rfkill->data, state); - if (!retval) { + if (!retval) rfkill->state = state; - rfkill_led_trigger(rfkill, state); - } } + if (rfkill->state != oldstate) + rfkill_led_trigger(rfkill, rfkill->state); + return retval; } @@ -105,6 +125,32 @@ void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state) } EXPORT_SYMBOL(rfkill_switch_all); +/** + * rfkill_force_state - Force the internal rfkill radio state + * @rfkill: pointer to the rfkill class to modify. + * @state: the current radio state the class should be forced to. + * + * This function updates the internal state of the radio cached + * by the rfkill class. It should be used when the driver gets + * a notification by the firmware/hardware of the current *real* + * state of the radio rfkill switch. + * + * It may not be called from an atomic context. + */ +int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state) +{ + if (state != RFKILL_STATE_OFF && + state != RFKILL_STATE_ON) + return -EINVAL; + + mutex_lock(&rfkill->mutex); + rfkill->state = state; + mutex_unlock(&rfkill->mutex); + + return 0; +} +EXPORT_SYMBOL(rfkill_force_state); + static ssize_t rfkill_name_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -147,6 +193,7 @@ static ssize_t rfkill_state_show(struct device *dev, { struct rfkill *rfkill = to_rfkill(dev); + update_rfkill_state(rfkill); return sprintf(buf, "%d\n", rfkill->state); } -- cgit v1.2.3 From 477576a073699783abb53ae14993d5d41c66301d Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:23:01 -0300 Subject: rfkill: add the WWAN radio type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unfortunately, instead of adding a generic Wireless WAN type, a technology- specific type (WiMAX) was added. That's useless for other WWAN devices, such as EDGE, UMTS, X-RTT and other such radios. Add a WWAN rfkill type for generic wireless WAN devices. No keys are added as most devices really want to use KEY_WLAN for WWAN control (in a cycle of none, WLAN, WWAN, WLAN+WWAN) and need no specific keycode added. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Cc: Iñaky Pérez-González Cc: David S. Miller Signed-off-by: John W. Linville --- include/linux/rfkill.h | 2 ++ net/rfkill/rfkill-input.c | 4 ++++ net/rfkill/rfkill.c | 3 +++ 3 files changed, 9 insertions(+) (limited to 'include/linux') diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h index 844e96114861..c0cab7d37828 100644 --- a/include/linux/rfkill.h +++ b/include/linux/rfkill.h @@ -34,12 +34,14 @@ * RFKILL_TYPE_BLUETOOTH: switch is on a bluetooth device. * RFKILL_TYPE_UWB: switch is on a ultra wideband device. * RFKILL_TYPE_WIMAX: switch is on a WiMAX device. + * RFKILL_TYPE_WWAN: switch is on a wireless WAN device. */ enum rfkill_type { RFKILL_TYPE_WLAN , RFKILL_TYPE_BLUETOOTH, RFKILL_TYPE_UWB, RFKILL_TYPE_WIMAX, + RFKILL_TYPE_WWAN, RFKILL_TYPE_MAX, }; diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c index 9d6c9255bf2c..29c13d308b31 100644 --- a/net/rfkill/rfkill-input.c +++ b/net/rfkill/rfkill-input.c @@ -101,6 +101,7 @@ static DEFINE_RFKILL_TASK(rfkill_wlan, RFKILL_TYPE_WLAN); static DEFINE_RFKILL_TASK(rfkill_bt, RFKILL_TYPE_BLUETOOTH); static DEFINE_RFKILL_TASK(rfkill_uwb, RFKILL_TYPE_UWB); static DEFINE_RFKILL_TASK(rfkill_wimax, RFKILL_TYPE_WIMAX); +static DEFINE_RFKILL_TASK(rfkill_wwan, RFKILL_TYPE_WWAN); static void rfkill_event(struct input_handle *handle, unsigned int type, unsigned int code, int data) @@ -126,6 +127,9 @@ static void rfkill_event(struct input_handle *handle, unsigned int type, switch (code) { case SW_RFKILL_ALL: /* EVERY radio type. data != 0 means radios ON */ + rfkill_schedule_set(&rfkill_wwan, + (data)? RFKILL_STATE_ON: + RFKILL_STATE_OFF); rfkill_schedule_set(&rfkill_wimax, (data)? RFKILL_STATE_ON: RFKILL_STATE_OFF); diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index 4ae4486c77ea..79f3bbb027ff 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -180,6 +180,9 @@ static ssize_t rfkill_type_show(struct device *dev, case RFKILL_TYPE_WIMAX: type = "wimax"; break; + case RFKILL_TYPE_WWAN: + type = "wwan"; + break; default: BUG(); } -- cgit v1.2.3 From 79399a8d1908f6a406e82d23c5a9937e1722ed3a Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:23:03 -0300 Subject: rfkill: add notifier chains support Add a notifier chain for use by the rfkill class. This notifier chain signals the following events (more to be added when needed): 1. rfkill: rfkill device state has changed A pointer to the rfkill struct will be passed as a parameter. The notifier message types have been added to include/linux/rfkill.h instead of to include/linux/notifier.h in order to avoid the madness of modifying a header used globally (and that triggers an almost full tree rebuild every time it is touched) with information that is of interest only to code that includes the rfkill.h header. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- include/linux/rfkill.h | 7 +++++ net/rfkill/rfkill.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 74 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h index c0cab7d37828..98667becdee4 100644 --- a/include/linux/rfkill.h +++ b/include/linux/rfkill.h @@ -117,4 +117,11 @@ static inline char *rfkill_get_led_name(struct rfkill *rfkill) #endif } +/* rfkill notification chain */ +#define RFKILL_STATE_CHANGED 0x0001 /* state of a normal rfkill + switch has changed */ + +int register_rfkill_notifier(struct notifier_block *nb); +int unregister_rfkill_notifier(struct notifier_block *nb); + #endif /* RFKILL_H */ diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index fb566902030a..a561e350a70a 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -46,6 +46,49 @@ MODULE_PARM_DESC(default_state, static enum rfkill_state rfkill_states[RFKILL_TYPE_MAX]; +static BLOCKING_NOTIFIER_HEAD(rfkill_notifier_list); + + +/** + * register_rfkill_notifier - Add notifier to rfkill notifier chain + * @nb: pointer to the new entry to add to the chain + * + * See blocking_notifier_chain_register() for return value and further + * observations. + * + * Adds a notifier to the rfkill notifier chain. The chain will be + * called with a pointer to the relevant rfkill structure as a parameter, + * refer to include/linux/rfkill.h for the possible events. + * + * Notifiers added to this chain are to always return NOTIFY_DONE. This + * chain is a blocking notifier chain: notifiers can sleep. + * + * Calls to this chain may have been done through a workqueue. One must + * assume unordered asynchronous behaviour, there is no way to know if + * actions related to the event that generated the notification have been + * carried out already. + */ +int register_rfkill_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&rfkill_notifier_list, nb); +} +EXPORT_SYMBOL_GPL(register_rfkill_notifier); + +/** + * unregister_rfkill_notifier - remove notifier from rfkill notifier chain + * @nb: pointer to the entry to remove from the chain + * + * See blocking_notifier_chain_unregister() for return value and further + * observations. + * + * Removes a notifier from the rfkill notifier chain. + */ +int unregister_rfkill_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&rfkill_notifier_list, nb); +} +EXPORT_SYMBOL_GPL(unregister_rfkill_notifier); + static void rfkill_led_trigger(struct rfkill *rfkill, enum rfkill_state state) @@ -62,14 +105,25 @@ static void rfkill_led_trigger(struct rfkill *rfkill, #endif /* CONFIG_RFKILL_LEDS */ } +static void notify_rfkill_state_change(struct rfkill *rfkill) +{ + blocking_notifier_call_chain(&rfkill_notifier_list, + RFKILL_STATE_CHANGED, + rfkill); +} + static void update_rfkill_state(struct rfkill *rfkill) { - enum rfkill_state newstate; + enum rfkill_state newstate, oldstate; if (rfkill->get_state) { mutex_lock(&rfkill->mutex); - if (!rfkill->get_state(rfkill->data, &newstate)) + if (!rfkill->get_state(rfkill->data, &newstate)) { + oldstate = rfkill->state; rfkill->state = newstate; + if (oldstate != newstate) + notify_rfkill_state_change(rfkill); + } mutex_unlock(&rfkill->mutex); } } @@ -93,8 +147,10 @@ static int rfkill_toggle_radio(struct rfkill *rfkill, rfkill->state = state; } - if (force || rfkill->state != oldstate) + if (force || rfkill->state != oldstate) { rfkill_led_trigger(rfkill, rfkill->state); + notify_rfkill_state_change(rfkill); + } return retval; } @@ -139,12 +195,20 @@ EXPORT_SYMBOL(rfkill_switch_all); */ int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state) { + enum rfkill_state oldstate; + if (state != RFKILL_STATE_OFF && state != RFKILL_STATE_ON) return -EINVAL; mutex_lock(&rfkill->mutex); + + oldstate = rfkill->state; rfkill->state = state; + + if (state != oldstate) + notify_rfkill_state_change(rfkill); + mutex_unlock(&rfkill->mutex); return 0; -- cgit v1.2.3 From 5005657cbd0fd6f277f807c0612a6b6d4396a02c Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:46:42 -0300 Subject: rfkill: rename the rfkill_state states and add block-locked state The current naming of rfkill_state causes a lot of confusion: not only the "kill" in rfkill suggests negative logic, but also the fact that rfkill cannot turn anything on (it can just force something off or stop forcing something off) is often forgotten. Rename RFKILL_STATE_OFF to RFKILL_STATE_SOFT_BLOCKED (transmitter is blocked and will not operate; state can be changed by a toggle_radio request), and RFKILL_STATE_ON to RFKILL_STATE_UNBLOCKED (transmitter is not blocked, and may operate). Also, add a new third state, RFKILL_STATE_HARD_BLOCKED (transmitter is blocked and will not operate; state cannot be changed through a toggle_radio request), which is used by drivers to indicate a wireless transmiter was blocked by a hardware rfkill line that accepts no overrides. Keep the old names as #defines, but document them as deprecated. This way, drivers can be converted to the new names *and* verified to actually use rfkill correctly one by one. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- Documentation/rfkill.txt | 56 +++++++++++++++++++++++++++++------ include/linux/rfkill.h | 32 ++++++++++++++++++-- net/rfkill/rfkill-input.c | 29 +++++++++--------- net/rfkill/rfkill.c | 75 ++++++++++++++++++++++++++++++++++++++--------- 4 files changed, 152 insertions(+), 40 deletions(-) (limited to 'include/linux') diff --git a/Documentation/rfkill.txt b/Documentation/rfkill.txt index cf230c1ad9ef..5316cea95ce0 100644 --- a/Documentation/rfkill.txt +++ b/Documentation/rfkill.txt @@ -60,9 +60,20 @@ The second option provides an rfkill input handler. This handler will listen to all rfkill key events and will toggle the radio accordingly. With this option enabled userspace could either do nothing or simply perform monitoring tasks. -When a rfkill switch is in the RFKILL_STATE_ON, the wireless transmitter (radio -TX circuit for example) is *enabled*. When the rfkill switch is in the -RFKILL_STATE_OFF, the wireless transmitter is to be *blocked* from operating. +When a rfkill switch is in the RFKILL_STATE_UNBLOCKED, the wireless transmitter +(radio TX circuit for example) is *enabled*. When the rfkill switch is in the +RFKILL_STATE_SOFT_BLOCKED or RFKILL_STATE_HARD_BLOCKED, the wireless +transmitter is to be *blocked* from operating. + +RFKILL_STATE_SOFT_BLOCKED indicates that a call to toggle_radio() can change +that state. RFKILL_STATE_HARD_BLOCKED indicates that a call to toggle_radio() +will not be able to change the state and will return with a suitable error if +attempts are made to set the state to RFKILL_STATE_UNBLOCKED. + +RFKILL_STATE_HARD_BLOCKED is used by drivers to signal that the device is +locked in the BLOCKED state by a hardwire rfkill line (typically an input pin +that, when active, forces the transmitter to be disabled) which the driver +CANNOT override. Full rfkill functionality requires two different subsystems to cooperate: the input layer and the rfkill class. The input layer issues *commands* to the @@ -122,10 +133,10 @@ Userspace input handlers (uevents) or kernel input handlers (rfkill-input): action). * rfkill-input implements EPO by handling EV_SW SW_RFKILL_ALL 0 (power off all transmitters) in a special way: it ignores any - overrides and local state cache and forces all transmitters to - the OFF state (including those which are already supposed to be - OFF). Note that the opposite event (power on all transmitters) - is handled normally. + overrides and local state cache and forces all transmitters to the + RFKILL_STATE_SOFT_BLOCKED state (including those which are already + supposed to be BLOCKED). Note that the opposite event (power on all + transmitters) is handled normally. Userspace uevent handler or kernel platform-specific drivers hooked to the rfkill notifier chain: @@ -284,6 +295,19 @@ You should: YOU CAN ACCESS state DIRECTLY) - rfkill_register() +The only way to set a device to the RFKILL_STATE_HARD_BLOCKED state is through +a suitable return of get_state() or through rfkill_force_state(). + +When a device is in the RFKILL_STATE_HARD_BLOCKED state, the only way to switch +it to a different state is through a suitable return of get_state() or through +rfkill_force_state(). + +If toggle_radio() is called to set a device to state RFKILL_STATE_SOFT_BLOCKED +when that device is already at the RFKILL_STATE_HARD_BLOCKED state, it should +not return an error. Instead, it should try to double-block the transmitter, +so that its state will change from RFKILL_STATE_HARD_BLOCKED to +RFKILL_STATE_SOFT_BLOCKED should the hardware blocking cease. + Please refer to the source for more documentation. =============================================================================== @@ -322,13 +346,27 @@ change by writing to the "state" attribute in order for anything to happen. Take particular care to implement EV_SW SW_RFKILL_ALL properly. When that switch is set to OFF, *every* rfkill device *MUST* be immediately put into the -OFF state, no questions asked. +RFKILL_STATE_SOFT_BLOCKED state, no questions asked. The following sysfs entries will be created: name: Name assigned by driver to this key (interface or driver name). type: Name of the key type ("wlan", "bluetooth", etc). - state: Current state of the key. 1: On, 0: Off. + state: Current state of the transmitter + 0: RFKILL_STATE_SOFT_BLOCKED + transmitter is forced off, but you can override it + by a write to the state attribute, or through input + events (if rfkill-input is loaded). + 1: RFKILL_STATE_UNBLOCKED + transmiter is NOT forced off, and may operate if + all other conditions for such operation are met + (such as interface is up and configured, etc). + 2: RFKILL_STATE_HARD_BLOCKED + transmitter is forced off by something outside of + the driver's control. + + You cannot set a device to this state through + writes to the state attribute. claim: 1: Userspace handles events, 0: Kernel handles events Both the "state" and "claim" entries are also writable. For the "state" entry diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h index 98667becdee4..c5f6e54ec6ae 100644 --- a/include/linux/rfkill.h +++ b/include/linux/rfkill.h @@ -46,16 +46,25 @@ enum rfkill_type { }; enum rfkill_state { - RFKILL_STATE_OFF = 0, /* Radio output blocked */ - RFKILL_STATE_ON = 1, /* Radio output active */ + RFKILL_STATE_SOFT_BLOCKED = 0, /* Radio output blocked */ + RFKILL_STATE_UNBLOCKED = 1, /* Radio output allowed */ + RFKILL_STATE_HARD_BLOCKED = 2, /* Output blocked, non-overrideable */ }; +/* + * These are DEPRECATED, drivers using them should be verified to + * comply with the rfkill usage guidelines in Documentation/rfkill.txt + * and then converted to use the new names for rfkill_state + */ +#define RFKILL_STATE_OFF RFKILL_STATE_SOFT_BLOCKED +#define RFKILL_STATE_ON RFKILL_STATE_UNBLOCKED + /** * struct rfkill - rfkill control structure. * @name: Name of the switch. * @type: Radio type which the button controls, the value stored * here should be a value from enum rfkill_type. - * @state: State of the switch, "ON" means radio can operate. + * @state: State of the switch, "UNBLOCKED" means radio can operate. * @user_claim_unsupported: Whether the hardware supports exclusive * RF-kill control by userspace. Set this before registering. * @user_claim: Set when the switch is controlled exlusively by userspace. @@ -63,8 +72,12 @@ enum rfkill_state { * @data: Pointer to the RF button drivers private data which will be * passed along when toggling radio state. * @toggle_radio(): Mandatory handler to control state of the radio. + * only RFKILL_STATE_SOFT_BLOCKED and RFKILL_STATE_UNBLOCKED are + * valid parameters. * @get_state(): handler to read current radio state from hardware, * may be called from atomic context, should return 0 on success. + * Either this handler OR judicious use of rfkill_force_state() is + * MANDATORY for any driver capable of RFKILL_STATE_HARD_BLOCKED. * @led_trigger: A LED trigger for this button's LED. * @dev: Device structure integrating the switch into device tree. * @node: Used to place switch into list of all switches known to the @@ -102,6 +115,19 @@ void rfkill_unregister(struct rfkill *rfkill); int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state); +/** + * rfkill_state_complement - return complementar state + * @state: state to return the complement of + * + * Returns RFKILL_STATE_SOFT_BLOCKED if @state is RFKILL_STATE_UNBLOCKED, + * returns RFKILL_STATE_UNBLOCKED otherwise. + */ +static inline enum rfkill_state rfkill_state_complement(enum rfkill_state state) +{ + return (state == RFKILL_STATE_UNBLOCKED) ? + RFKILL_STATE_SOFT_BLOCKED : RFKILL_STATE_UNBLOCKED; +} + /** * rfkill_get_led_name - Get the LED trigger name for the button's LED. * This function might return a NULL pointer if registering of the diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c index 5d4c8b2446f7..8aa822730145 100644 --- a/net/rfkill/rfkill-input.c +++ b/net/rfkill/rfkill-input.c @@ -84,7 +84,8 @@ static void rfkill_schedule_toggle(struct rfkill_task *task) spin_lock_irqsave(&task->lock, flags); if (time_after(jiffies, task->last + msecs_to_jiffies(200))) { - task->desired_state = !task->desired_state; + task->desired_state = + rfkill_state_complement(task->desired_state); task->last = jiffies; schedule_work(&task->work); } @@ -92,14 +93,14 @@ static void rfkill_schedule_toggle(struct rfkill_task *task) spin_unlock_irqrestore(&task->lock, flags); } -#define DEFINE_RFKILL_TASK(n, t) \ - struct rfkill_task n = { \ - .work = __WORK_INITIALIZER(n.work, \ - rfkill_task_handler), \ - .type = t, \ - .mutex = __MUTEX_INITIALIZER(n.mutex), \ - .lock = __SPIN_LOCK_UNLOCKED(n.lock), \ - .desired_state = RFKILL_STATE_ON, \ +#define DEFINE_RFKILL_TASK(n, t) \ + struct rfkill_task n = { \ + .work = __WORK_INITIALIZER(n.work, \ + rfkill_task_handler), \ + .type = t, \ + .mutex = __MUTEX_INITIALIZER(n.mutex), \ + .lock = __SPIN_LOCK_UNLOCKED(n.lock), \ + .desired_state = RFKILL_STATE_UNBLOCKED, \ } static DEFINE_RFKILL_TASK(rfkill_wlan, RFKILL_TYPE_WLAN); @@ -135,15 +136,15 @@ static void rfkill_event(struct input_handle *handle, unsigned int type, /* handle EPO (emergency power off) through shortcut */ if (data) { rfkill_schedule_set(&rfkill_wwan, - RFKILL_STATE_ON); + RFKILL_STATE_UNBLOCKED); rfkill_schedule_set(&rfkill_wimax, - RFKILL_STATE_ON); + RFKILL_STATE_UNBLOCKED); rfkill_schedule_set(&rfkill_uwb, - RFKILL_STATE_ON); + RFKILL_STATE_UNBLOCKED); rfkill_schedule_set(&rfkill_bt, - RFKILL_STATE_ON); + RFKILL_STATE_UNBLOCKED); rfkill_schedule_set(&rfkill_wlan, - RFKILL_STATE_ON); + RFKILL_STATE_UNBLOCKED); } else rfkill_schedule_epo(); break; diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index 7d07175c407f..ce0e23148cdd 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -39,7 +39,7 @@ MODULE_LICENSE("GPL"); static LIST_HEAD(rfkill_list); /* list of registered rf switches */ static DEFINE_MUTEX(rfkill_mutex); -static unsigned int rfkill_default_state = RFKILL_STATE_ON; +static unsigned int rfkill_default_state = RFKILL_STATE_UNBLOCKED; module_param_named(default_state, rfkill_default_state, uint, 0444); MODULE_PARM_DESC(default_state, "Default initial state for all radio types, 0 = radio off"); @@ -98,7 +98,7 @@ static void rfkill_led_trigger(struct rfkill *rfkill, if (!led->name) return; - if (state == RFKILL_STATE_OFF) + if (state != RFKILL_STATE_UNBLOCKED) led_trigger_event(led, LED_OFF); else led_trigger_event(led, LED_FULL); @@ -128,6 +128,28 @@ static void update_rfkill_state(struct rfkill *rfkill) } } +/** + * rfkill_toggle_radio - wrapper for toggle_radio hook + * calls toggle_radio taking into account a lot of "small" + * details. + * @rfkill: the rfkill struct to use + * @force: calls toggle_radio even if cache says it is not needed, + * and also makes sure notifications of the state will be + * sent even if it didn't change + * @state: the new state to call toggle_radio() with + * + * This wrappen protects and enforces the API for toggle_radio + * calls. Note that @force cannot override a (possibly cached) + * state of RFKILL_STATE_HARD_BLOCKED. Any device making use of + * RFKILL_STATE_HARD_BLOCKED implements either get_state() or + * rfkill_force_state(), so the cache either is bypassed or valid. + * + * Note that we do call toggle_radio for RFKILL_STATE_SOFT_BLOCKED + * even if the radio is in RFKILL_STATE_HARD_BLOCKED state, so as to + * give the driver a hint that it should double-BLOCK the transmitter. + * + * Caller must have aquired rfkill_mutex. + */ static int rfkill_toggle_radio(struct rfkill *rfkill, enum rfkill_state state, int force) @@ -141,9 +163,28 @@ static int rfkill_toggle_radio(struct rfkill *rfkill, !rfkill->get_state(rfkill->data, &newstate)) rfkill->state = newstate; + switch (state) { + case RFKILL_STATE_HARD_BLOCKED: + /* typically happens when refreshing hardware state, + * such as on resume */ + state = RFKILL_STATE_SOFT_BLOCKED; + break; + case RFKILL_STATE_UNBLOCKED: + /* force can't override this, only rfkill_force_state() can */ + if (rfkill->state == RFKILL_STATE_HARD_BLOCKED) + return -EPERM; + break; + case RFKILL_STATE_SOFT_BLOCKED: + /* nothing to do, we want to give drivers the hint to double + * BLOCK even a transmitter that is already in state + * RFKILL_STATE_HARD_BLOCKED */ + break; + } + if (force || state != rfkill->state) { retval = rfkill->toggle_radio(rfkill->data, state); - if (!retval) + /* never allow a HARD->SOFT downgrade! */ + if (!retval && rfkill->state != RFKILL_STATE_HARD_BLOCKED) rfkill->state = state; } @@ -184,7 +225,7 @@ EXPORT_SYMBOL(rfkill_switch_all); /** * rfkill_epo - emergency power off all transmitters * - * This kicks all rfkill devices to RFKILL_STATE_OFF, ignoring + * This kicks all rfkill devices to RFKILL_STATE_SOFT_BLOCKED, ignoring * everything in its path but rfkill_mutex. */ void rfkill_epo(void) @@ -193,7 +234,7 @@ void rfkill_epo(void) mutex_lock(&rfkill_mutex); list_for_each_entry(rfkill, &rfkill_list, node) { - rfkill_toggle_radio(rfkill, RFKILL_STATE_OFF, 1); + rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1); } mutex_unlock(&rfkill_mutex); } @@ -215,8 +256,9 @@ int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state) { enum rfkill_state oldstate; - if (state != RFKILL_STATE_OFF && - state != RFKILL_STATE_ON) + if (state != RFKILL_STATE_SOFT_BLOCKED && + state != RFKILL_STATE_UNBLOCKED && + state != RFKILL_STATE_HARD_BLOCKED) return -EINVAL; mutex_lock(&rfkill->mutex); @@ -290,11 +332,14 @@ static ssize_t rfkill_state_store(struct device *dev, if (!capable(CAP_NET_ADMIN)) return -EPERM; + /* RFKILL_STATE_HARD_BLOCKED is illegal here... */ + if (state != RFKILL_STATE_UNBLOCKED && + state != RFKILL_STATE_SOFT_BLOCKED) + return -EINVAL; + if (mutex_lock_interruptible(&rfkill->mutex)) return -ERESTARTSYS; - error = rfkill_toggle_radio(rfkill, - state ? RFKILL_STATE_ON : RFKILL_STATE_OFF, - 0); + error = rfkill_toggle_radio(rfkill, state, 0); mutex_unlock(&rfkill->mutex); return error ? error : count; @@ -373,7 +418,8 @@ static int rfkill_suspend(struct device *dev, pm_message_t state) update_rfkill_state(rfkill); mutex_lock(&rfkill->mutex); - rfkill->toggle_radio(rfkill->data, RFKILL_STATE_OFF); + rfkill->toggle_radio(rfkill->data, + RFKILL_STATE_SOFT_BLOCKED); mutex_unlock(&rfkill->mutex); } @@ -470,7 +516,7 @@ static void rfkill_remove_switch(struct rfkill *rfkill) { mutex_lock(&rfkill_mutex); list_del_init(&rfkill->node); - rfkill_toggle_radio(rfkill, RFKILL_STATE_OFF, 1); + rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1); mutex_unlock(&rfkill_mutex); } @@ -610,8 +656,9 @@ static int __init rfkill_init(void) int error; int i; - if (rfkill_default_state != RFKILL_STATE_OFF && - rfkill_default_state != RFKILL_STATE_ON) + /* RFKILL_STATE_HARD_BLOCKED is illegal here... */ + if (rfkill_default_state != RFKILL_STATE_SOFT_BLOCKED && + rfkill_default_state != RFKILL_STATE_UNBLOCKED) return -EINVAL; for (i = 0; i < ARRAY_SIZE(rfkill_states); i++) -- cgit v1.2.3 From f2df38596a81b6c24f4586b0b4befeaebf3e02db Mon Sep 17 00:00:00 2001 From: Assaf Krauss Date: Sun, 15 Jun 2008 18:23:29 +0300 Subject: mac80211: 11h Infrastructure - Parsing This patch introduces parsing of 11h and 11d related elements from incoming management frames. Signed-off-by: Assaf Krauss Signed-off-by: Tomas Winkler Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 54 +++++++++++++++++++++++++++++++++++++++++++--- net/mac80211/ieee80211_i.h | 9 ++++++++ net/mac80211/mlme.c | 19 ++++++++++++++++ 3 files changed, 79 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 2998e3b5f166..8546f09e462c 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -469,6 +469,40 @@ struct ieee80211s_hdr { u8 eaddr3[6]; } __attribute__ ((packed)); +/** + * struct ieee80211_quiet_ie + * + * This structure refers to "Quiet information element" + */ +struct ieee80211_quiet_ie { + u8 count; + u8 period; + __le16 duration; + __le16 offset; +} __attribute__ ((packed)); + +/** + * struct ieee80211_msrment_ie + * + * This structure refers to "Measurement Request/Report information element" + */ +struct ieee80211_msrment_ie { + u8 token; + u8 mode; + u8 type; + u8 request[0]; +} __attribute__ ((packed)); + +/** + * struct ieee80211_channel_sw_ie + * + * This structure refers to "Channel Switch Announcement information element" + */ +struct ieee80211_channel_sw_ie { + u8 mode; + u8 new_ch_num; + u8 count; +} __attribute__ ((packed)); struct ieee80211_mgmt { __le16 frame_control; @@ -544,10 +578,15 @@ struct ieee80211_mgmt { u8 action_code; u8 element_id; u8 length; - u8 switch_mode; - u8 new_chan; - u8 switch_count; + struct ieee80211_channel_sw_ie sw_elem; } __attribute__((packed)) chan_switch; + struct{ + u8 action_code; + u8 dialog_token; + u8 element_id; + u8 length; + struct ieee80211_msrment_ie msr_elem; + } __attribute__((packed)) measurement; struct{ u8 action_code; u8 dialog_token; @@ -875,6 +914,15 @@ enum ieee80211_category { WLAN_CATEGORY_WMM = 17, }; +/* SPECTRUM_MGMT action code */ +enum ieee80211_spectrum_mgmt_actioncode { + WLAN_ACTION_SPCT_MSR_REQ = 0, + WLAN_ACTION_SPCT_MSR_RPRT = 1, + WLAN_ACTION_SPCT_TPC_REQ = 2, + WLAN_ACTION_SPCT_TPC_RPRT = 3, + WLAN_ACTION_SPCT_CHL_SWITCH = 4, +}; + /* BACK action code */ enum ieee80211_back_actioncode { WLAN_ACTION_ADDBA_REQ = 0, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 80a9e7c07b47..af352c05c983 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -791,6 +791,10 @@ struct ieee802_11_elems { u8 *preq; u8 *prep; u8 *perr; + u8 *ch_switch_elem; + u8 *country_elem; + u8 *pwr_constr_elem; + u8 *quiet_elem; /* first quite element */ /* length of them, respectively */ u8 ssid_len; @@ -815,6 +819,11 @@ struct ieee802_11_elems { u8 preq_len; u8 prep_len; u8 perr_len; + u8 ch_switch_elem_len; + u8 country_elem_len; + u8 pwr_constr_elem_len; + u8 quiet_elem_len; + u8 num_of_quiet_elem; /* can be more the one */ }; static inline struct ieee80211_local *hw_to_local( diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index e06d6450f215..32453561fe32 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -204,6 +204,25 @@ void ieee802_11_parse_elems(u8 *start, size_t len, elems->perr = pos; elems->perr_len = elen; break; + case WLAN_EID_CHANNEL_SWITCH: + elems->ch_switch_elem = pos; + elems->ch_switch_elem_len = elen; + break; + case WLAN_EID_QUIET: + if (!elems->quiet_elem) { + elems->quiet_elem = pos; + elems->quiet_elem_len = elen; + } + elems->num_of_quiet_elem++; + break; + case WLAN_EID_COUNTRY: + elems->country_elem = pos; + elems->country_elem_len = elen; + break; + case WLAN_EID_PWR_CONSTRAINT: + elems->pwr_constr_elem = pos; + elems->pwr_constr_elem_len = elen; + break; default: break; } -- cgit v1.2.3 From b662348662f9661f9259c7186c1bdb65620045f1 Mon Sep 17 00:00:00 2001 From: Assaf Krauss Date: Mon, 16 Jun 2008 16:09:49 +0300 Subject: mac80211: 11h - Handling measurement request This patch handles the 11h measurement request information element. This is minimal requested implementation - refuse measurement. Signed-off-by: Assaf Krauss Signed-off-by: Tomas Winkler Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 10 ++++++ net/mac80211/mlme.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 8546f09e462c..cffd6d0094f9 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -739,11 +739,21 @@ struct ieee80211_ht_addt_info { #define WLAN_CAPABILITY_SHORT_PREAMBLE (1<<5) #define WLAN_CAPABILITY_PBCC (1<<6) #define WLAN_CAPABILITY_CHANNEL_AGILITY (1<<7) + /* 802.11h */ #define WLAN_CAPABILITY_SPECTRUM_MGMT (1<<8) #define WLAN_CAPABILITY_QOS (1<<9) #define WLAN_CAPABILITY_SHORT_SLOT_TIME (1<<10) #define WLAN_CAPABILITY_DSSS_OFDM (1<<13) +/* measurement */ +#define IEEE80211_SPCT_MSR_RPRT_MODE_LATE (1<<0) +#define IEEE80211_SPCT_MSR_RPRT_MODE_INCAPABLE (1<<1) +#define IEEE80211_SPCT_MSR_RPRT_MODE_REFUSED (1<<2) + +#define IEEE80211_SPCT_MSR_RPRT_TYPE_BASIC 0 +#define IEEE80211_SPCT_MSR_RPRT_TYPE_CCA 1 +#define IEEE80211_SPCT_MSR_RPRT_TYPE_RPI 2 + /* 802.11g ERP information element */ #define WLAN_ERP_NON_ERP_PRESENT (1<<0) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 32453561fe32..6d9a4facf9dd 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1720,6 +1720,71 @@ void ieee80211_sta_tear_down_BA_sessions(struct net_device *dev, u8 *addr) } } +static void ieee80211_send_refuse_measurement_request(struct net_device *dev, + struct ieee80211_msrment_ie *request_ie, + const u8 *da, const u8 *bssid, + u8 dialog_token) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct sk_buff *skb; + struct ieee80211_mgmt *msr_report; + + skb = dev_alloc_skb(sizeof(*msr_report) + local->hw.extra_tx_headroom + + sizeof(struct ieee80211_msrment_ie)); + + if (!skb) { + printk(KERN_ERR "%s: failed to allocate buffer for " + "measurement report frame\n", dev->name); + return; + } + + skb_reserve(skb, local->hw.extra_tx_headroom); + msr_report = (struct ieee80211_mgmt *)skb_put(skb, 24); + memset(msr_report, 0, 24); + memcpy(msr_report->da, da, ETH_ALEN); + memcpy(msr_report->sa, dev->dev_addr, ETH_ALEN); + memcpy(msr_report->bssid, bssid, ETH_ALEN); + msr_report->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT, + IEEE80211_STYPE_ACTION); + + skb_put(skb, 1 + sizeof(msr_report->u.action.u.measurement)); + msr_report->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT; + msr_report->u.action.u.measurement.action_code = + WLAN_ACTION_SPCT_MSR_RPRT; + msr_report->u.action.u.measurement.dialog_token = dialog_token; + + msr_report->u.action.u.measurement.element_id = WLAN_EID_MEASURE_REPORT; + msr_report->u.action.u.measurement.length = + sizeof(struct ieee80211_msrment_ie); + + memset(&msr_report->u.action.u.measurement.msr_elem, 0, + sizeof(struct ieee80211_msrment_ie)); + msr_report->u.action.u.measurement.msr_elem.token = request_ie->token; + msr_report->u.action.u.measurement.msr_elem.mode |= + IEEE80211_SPCT_MSR_RPRT_MODE_REFUSED; + msr_report->u.action.u.measurement.msr_elem.type = request_ie->type; + + ieee80211_sta_tx(dev, skb, 0); +} + +static void ieee80211_sta_process_measurement_req(struct net_device *dev, + struct ieee80211_mgmt *mgmt, + size_t len) +{ + /* + * Ignoring measurement request is spec violation. + * Mandatory measurements must be reported optional + * measurements might be refused or reported incapable + * For now just refuse + * TODO: Answer basic measurement as unmeasured + */ + ieee80211_send_refuse_measurement_request(dev, + &mgmt->u.action.u.measurement.msr_elem, + mgmt->sa, mgmt->bssid, + mgmt->u.action.u.measurement.dialog_token); +} + + static void ieee80211_rx_mgmt_auth(struct net_device *dev, struct ieee80211_if_sta *ifsta, struct ieee80211_mgmt *mgmt, @@ -3044,11 +3109,24 @@ static void ieee80211_rx_mgmt_action(struct net_device *dev, struct ieee80211_rx_status *rx_status) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); if (len < IEEE80211_MIN_ACTION_SIZE) return; switch (mgmt->u.action.category) { + case WLAN_CATEGORY_SPECTRUM_MGMT: + if (local->hw.conf.channel->band != IEEE80211_BAND_5GHZ) + break; + switch (mgmt->u.action.u.chan_switch.action_code) { + case WLAN_ACTION_SPCT_MSR_REQ: + if (len < (IEEE80211_MIN_ACTION_SIZE + + sizeof(mgmt->u.action.u.measurement))) + break; + ieee80211_sta_process_measurement_req(dev, mgmt, len); + break; + } + break; case WLAN_CATEGORY_BACK: switch (mgmt->u.action.u.addba_req.action_code) { case WLAN_ACTION_ADDBA_REQ: -- cgit v1.2.3 From f225763a7d6c92c4932dbd528437997078496fcc Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Fri, 20 Jun 2008 11:50:29 +0200 Subject: ssb, b43, b43legacy, b44: Rewrite SSB DMA API This is a rewrite of the DMA API for SSB devices. This is needed, because the old (non-existing) "API" made too many bad assumptions on the API of the host-bus (PCI). This introduces an almost complete SSB-DMA-API that maps to the lowlevel bus-API based on the bustype. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- drivers/net/b44.c | 140 +++++++++++++++++----------------- drivers/net/wireless/b43/dma.c | 65 ++++++++-------- drivers/net/wireless/b43legacy/dma.c | 63 ++++++++------- drivers/ssb/Kconfig | 2 +- drivers/ssb/main.c | 75 ++++++++++++++---- include/linux/ssb/ssb.h | 143 ++++++++++++++++++++++++++++++++++- 6 files changed, 336 insertions(+), 152 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/b44.c b/drivers/net/b44.c index 59dce6aa0865..c3bda5ce67c4 100644 --- a/drivers/net/b44.c +++ b/drivers/net/b44.c @@ -148,9 +148,9 @@ static inline void b44_sync_dma_desc_for_device(struct ssb_device *sdev, unsigned long offset, enum dma_data_direction dir) { - dma_sync_single_range_for_device(sdev->dma_dev, dma_base, - offset & dma_desc_align_mask, - dma_desc_sync_size, dir); + ssb_dma_sync_single_range_for_device(sdev, dma_base, + offset & dma_desc_align_mask, + dma_desc_sync_size, dir); } static inline void b44_sync_dma_desc_for_cpu(struct ssb_device *sdev, @@ -158,9 +158,9 @@ static inline void b44_sync_dma_desc_for_cpu(struct ssb_device *sdev, unsigned long offset, enum dma_data_direction dir) { - dma_sync_single_range_for_cpu(sdev->dma_dev, dma_base, - offset & dma_desc_align_mask, - dma_desc_sync_size, dir); + ssb_dma_sync_single_range_for_cpu(sdev, dma_base, + offset & dma_desc_align_mask, + dma_desc_sync_size, dir); } static inline unsigned long br32(const struct b44 *bp, unsigned long reg) @@ -613,10 +613,10 @@ static void b44_tx(struct b44 *bp) BUG_ON(skb == NULL); - dma_unmap_single(bp->sdev->dma_dev, - rp->mapping, - skb->len, - DMA_TO_DEVICE); + ssb_dma_unmap_single(bp->sdev, + rp->mapping, + skb->len, + DMA_TO_DEVICE); rp->skb = NULL; dev_kfree_skb_irq(skb); } @@ -653,29 +653,29 @@ static int b44_alloc_rx_skb(struct b44 *bp, int src_idx, u32 dest_idx_unmasked) if (skb == NULL) return -ENOMEM; - mapping = dma_map_single(bp->sdev->dma_dev, skb->data, - RX_PKT_BUF_SZ, - DMA_FROM_DEVICE); + mapping = ssb_dma_map_single(bp->sdev, skb->data, + RX_PKT_BUF_SZ, + DMA_FROM_DEVICE); /* Hardware bug work-around, the chip is unable to do PCI DMA to/from anything above 1GB :-( */ - if (dma_mapping_error(mapping) || + if (ssb_dma_mapping_error(bp->sdev, mapping) || mapping + RX_PKT_BUF_SZ > DMA_30BIT_MASK) { /* Sigh... */ - if (!dma_mapping_error(mapping)) - dma_unmap_single(bp->sdev->dma_dev, mapping, - RX_PKT_BUF_SZ, DMA_FROM_DEVICE); + if (!ssb_dma_mapping_error(bp->sdev, mapping)) + ssb_dma_unmap_single(bp->sdev, mapping, + RX_PKT_BUF_SZ, DMA_FROM_DEVICE); dev_kfree_skb_any(skb); skb = __netdev_alloc_skb(bp->dev, RX_PKT_BUF_SZ, GFP_ATOMIC|GFP_DMA); if (skb == NULL) return -ENOMEM; - mapping = dma_map_single(bp->sdev->dma_dev, skb->data, - RX_PKT_BUF_SZ, - DMA_FROM_DEVICE); - if (dma_mapping_error(mapping) || + mapping = ssb_dma_map_single(bp->sdev, skb->data, + RX_PKT_BUF_SZ, + DMA_FROM_DEVICE); + if (ssb_dma_mapping_error(bp->sdev, mapping) || mapping + RX_PKT_BUF_SZ > DMA_30BIT_MASK) { - if (!dma_mapping_error(mapping)) - dma_unmap_single(bp->sdev->dma_dev, mapping, RX_PKT_BUF_SZ,DMA_FROM_DEVICE); + if (!ssb_dma_mapping_error(bp->sdev, mapping)) + ssb_dma_unmap_single(bp->sdev, mapping, RX_PKT_BUF_SZ,DMA_FROM_DEVICE); dev_kfree_skb_any(skb); return -ENOMEM; } @@ -750,9 +750,9 @@ static void b44_recycle_rx(struct b44 *bp, int src_idx, u32 dest_idx_unmasked) dest_idx * sizeof(dest_desc), DMA_BIDIRECTIONAL); - dma_sync_single_for_device(bp->sdev->dma_dev, le32_to_cpu(src_desc->addr), - RX_PKT_BUF_SZ, - DMA_FROM_DEVICE); + ssb_dma_sync_single_for_device(bp->sdev, le32_to_cpu(src_desc->addr), + RX_PKT_BUF_SZ, + DMA_FROM_DEVICE); } static int b44_rx(struct b44 *bp, int budget) @@ -772,7 +772,7 @@ static int b44_rx(struct b44 *bp, int budget) struct rx_header *rh; u16 len; - dma_sync_single_for_cpu(bp->sdev->dma_dev, map, + ssb_dma_sync_single_for_cpu(bp->sdev, map, RX_PKT_BUF_SZ, DMA_FROM_DEVICE); rh = (struct rx_header *) skb->data; @@ -806,8 +806,8 @@ static int b44_rx(struct b44 *bp, int budget) skb_size = b44_alloc_rx_skb(bp, cons, bp->rx_prod); if (skb_size < 0) goto drop_it; - dma_unmap_single(bp->sdev->dma_dev, map, - skb_size, DMA_FROM_DEVICE); + ssb_dma_unmap_single(bp->sdev, map, + skb_size, DMA_FROM_DEVICE); /* Leave out rx_header */ skb_put(skb, len + RX_PKT_OFFSET); skb_pull(skb, RX_PKT_OFFSET); @@ -966,25 +966,25 @@ static int b44_start_xmit(struct sk_buff *skb, struct net_device *dev) goto err_out; } - mapping = dma_map_single(bp->sdev->dma_dev, skb->data, len, DMA_TO_DEVICE); - if (dma_mapping_error(mapping) || mapping + len > DMA_30BIT_MASK) { + mapping = ssb_dma_map_single(bp->sdev, skb->data, len, DMA_TO_DEVICE); + if (ssb_dma_mapping_error(bp->sdev, mapping) || mapping + len > DMA_30BIT_MASK) { struct sk_buff *bounce_skb; /* Chip can't handle DMA to/from >1GB, use bounce buffer */ - if (!dma_mapping_error(mapping)) - dma_unmap_single(bp->sdev->dma_dev, mapping, len, - DMA_TO_DEVICE); + if (!ssb_dma_mapping_error(bp->sdev, mapping)) + ssb_dma_unmap_single(bp->sdev, mapping, len, + DMA_TO_DEVICE); bounce_skb = __dev_alloc_skb(len, GFP_ATOMIC | GFP_DMA); if (!bounce_skb) goto err_out; - mapping = dma_map_single(bp->sdev->dma_dev, bounce_skb->data, - len, DMA_TO_DEVICE); - if (dma_mapping_error(mapping) || mapping + len > DMA_30BIT_MASK) { - if (!dma_mapping_error(mapping)) - dma_unmap_single(bp->sdev->dma_dev, mapping, - len, DMA_TO_DEVICE); + mapping = ssb_dma_map_single(bp->sdev, bounce_skb->data, + len, DMA_TO_DEVICE); + if (ssb_dma_mapping_error(bp->sdev, mapping) || mapping + len > DMA_30BIT_MASK) { + if (!ssb_dma_mapping_error(bp->sdev, mapping)) + ssb_dma_unmap_single(bp->sdev, mapping, + len, DMA_TO_DEVICE); dev_kfree_skb_any(bounce_skb); goto err_out; } @@ -1082,8 +1082,8 @@ static void b44_free_rings(struct b44 *bp) if (rp->skb == NULL) continue; - dma_unmap_single(bp->sdev->dma_dev, rp->mapping, RX_PKT_BUF_SZ, - DMA_FROM_DEVICE); + ssb_dma_unmap_single(bp->sdev, rp->mapping, RX_PKT_BUF_SZ, + DMA_FROM_DEVICE); dev_kfree_skb_any(rp->skb); rp->skb = NULL; } @@ -1094,8 +1094,8 @@ static void b44_free_rings(struct b44 *bp) if (rp->skb == NULL) continue; - dma_unmap_single(bp->sdev->dma_dev, rp->mapping, rp->skb->len, - DMA_TO_DEVICE); + ssb_dma_unmap_single(bp->sdev, rp->mapping, rp->skb->len, + DMA_TO_DEVICE); dev_kfree_skb_any(rp->skb); rp->skb = NULL; } @@ -1117,14 +1117,14 @@ static void b44_init_rings(struct b44 *bp) memset(bp->tx_ring, 0, B44_TX_RING_BYTES); if (bp->flags & B44_FLAG_RX_RING_HACK) - dma_sync_single_for_device(bp->sdev->dma_dev, bp->rx_ring_dma, - DMA_TABLE_BYTES, - DMA_BIDIRECTIONAL); + ssb_dma_sync_single_for_device(bp->sdev, bp->rx_ring_dma, + DMA_TABLE_BYTES, + DMA_BIDIRECTIONAL); if (bp->flags & B44_FLAG_TX_RING_HACK) - dma_sync_single_for_device(bp->sdev->dma_dev, bp->tx_ring_dma, - DMA_TABLE_BYTES, - DMA_TO_DEVICE); + ssb_dma_sync_single_for_device(bp->sdev, bp->tx_ring_dma, + DMA_TABLE_BYTES, + DMA_TO_DEVICE); for (i = 0; i < bp->rx_pending; i++) { if (b44_alloc_rx_skb(bp, -1, i) < 0) @@ -1144,25 +1144,27 @@ static void b44_free_consistent(struct b44 *bp) bp->tx_buffers = NULL; if (bp->rx_ring) { if (bp->flags & B44_FLAG_RX_RING_HACK) { - dma_unmap_single(bp->sdev->dma_dev, bp->rx_ring_dma, - DMA_TABLE_BYTES, - DMA_BIDIRECTIONAL); + ssb_dma_unmap_single(bp->sdev, bp->rx_ring_dma, + DMA_TABLE_BYTES, + DMA_BIDIRECTIONAL); kfree(bp->rx_ring); } else - dma_free_coherent(bp->sdev->dma_dev, DMA_TABLE_BYTES, - bp->rx_ring, bp->rx_ring_dma); + ssb_dma_free_consistent(bp->sdev, DMA_TABLE_BYTES, + bp->rx_ring, bp->rx_ring_dma, + GFP_KERNEL); bp->rx_ring = NULL; bp->flags &= ~B44_FLAG_RX_RING_HACK; } if (bp->tx_ring) { if (bp->flags & B44_FLAG_TX_RING_HACK) { - dma_unmap_single(bp->sdev->dma_dev, bp->tx_ring_dma, - DMA_TABLE_BYTES, - DMA_TO_DEVICE); + ssb_dma_unmap_single(bp->sdev, bp->tx_ring_dma, + DMA_TABLE_BYTES, + DMA_TO_DEVICE); kfree(bp->tx_ring); } else - dma_free_coherent(bp->sdev->dma_dev, DMA_TABLE_BYTES, - bp->tx_ring, bp->tx_ring_dma); + ssb_dma_free_consistent(bp->sdev, DMA_TABLE_BYTES, + bp->tx_ring, bp->tx_ring_dma, + GFP_KERNEL); bp->tx_ring = NULL; bp->flags &= ~B44_FLAG_TX_RING_HACK; } @@ -1187,7 +1189,7 @@ static int b44_alloc_consistent(struct b44 *bp, gfp_t gfp) goto out_err; size = DMA_TABLE_BYTES; - bp->rx_ring = dma_alloc_coherent(bp->sdev->dma_dev, size, &bp->rx_ring_dma, gfp); + bp->rx_ring = ssb_dma_alloc_consistent(bp->sdev, size, &bp->rx_ring_dma, gfp); if (!bp->rx_ring) { /* Allocation may have failed due to pci_alloc_consistent insisting on use of GFP_DMA, which is more restrictive @@ -1199,11 +1201,11 @@ static int b44_alloc_consistent(struct b44 *bp, gfp_t gfp) if (!rx_ring) goto out_err; - rx_ring_dma = dma_map_single(bp->sdev->dma_dev, rx_ring, - DMA_TABLE_BYTES, - DMA_BIDIRECTIONAL); + rx_ring_dma = ssb_dma_map_single(bp->sdev, rx_ring, + DMA_TABLE_BYTES, + DMA_BIDIRECTIONAL); - if (dma_mapping_error(rx_ring_dma) || + if (ssb_dma_mapping_error(bp->sdev, rx_ring_dma) || rx_ring_dma + size > DMA_30BIT_MASK) { kfree(rx_ring); goto out_err; @@ -1214,9 +1216,9 @@ static int b44_alloc_consistent(struct b44 *bp, gfp_t gfp) bp->flags |= B44_FLAG_RX_RING_HACK; } - bp->tx_ring = dma_alloc_coherent(bp->sdev->dma_dev, size, &bp->tx_ring_dma, gfp); + bp->tx_ring = ssb_dma_alloc_consistent(bp->sdev, size, &bp->tx_ring_dma, gfp); if (!bp->tx_ring) { - /* Allocation may have failed due to dma_alloc_coherent + /* Allocation may have failed due to ssb_dma_alloc_consistent insisting on use of GFP_DMA, which is more restrictive than necessary... */ struct dma_desc *tx_ring; @@ -1226,11 +1228,11 @@ static int b44_alloc_consistent(struct b44 *bp, gfp_t gfp) if (!tx_ring) goto out_err; - tx_ring_dma = dma_map_single(bp->sdev->dma_dev, tx_ring, + tx_ring_dma = ssb_dma_map_single(bp->sdev, tx_ring, DMA_TABLE_BYTES, DMA_TO_DEVICE); - if (dma_mapping_error(tx_ring_dma) || + if (ssb_dma_mapping_error(bp->sdev, tx_ring_dma) || tx_ring_dma + size > DMA_30BIT_MASK) { kfree(tx_ring); goto out_err; diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/b43/dma.c index 8a09a1db08db..098f886976f6 100644 --- a/drivers/net/wireless/b43/dma.c +++ b/drivers/net/wireless/b43/dma.c @@ -328,11 +328,11 @@ static inline dma_addr_t dmaaddr; if (tx) { - dmaaddr = dma_map_single(ring->dev->dev->dma_dev, - buf, len, DMA_TO_DEVICE); + dmaaddr = ssb_dma_map_single(ring->dev->dev, + buf, len, DMA_TO_DEVICE); } else { - dmaaddr = dma_map_single(ring->dev->dev->dma_dev, - buf, len, DMA_FROM_DEVICE); + dmaaddr = ssb_dma_map_single(ring->dev->dev, + buf, len, DMA_FROM_DEVICE); } return dmaaddr; @@ -343,11 +343,11 @@ static inline dma_addr_t addr, size_t len, int tx) { if (tx) { - dma_unmap_single(ring->dev->dev->dma_dev, - addr, len, DMA_TO_DEVICE); + ssb_dma_unmap_single(ring->dev->dev, + addr, len, DMA_TO_DEVICE); } else { - dma_unmap_single(ring->dev->dev->dma_dev, - addr, len, DMA_FROM_DEVICE); + ssb_dma_unmap_single(ring->dev->dev, + addr, len, DMA_FROM_DEVICE); } } @@ -356,8 +356,8 @@ static inline dma_addr_t addr, size_t len) { B43_WARN_ON(ring->tx); - dma_sync_single_for_cpu(ring->dev->dev->dma_dev, - addr, len, DMA_FROM_DEVICE); + ssb_dma_sync_single_for_cpu(ring->dev->dev, + addr, len, DMA_FROM_DEVICE); } static inline @@ -365,8 +365,8 @@ static inline dma_addr_t addr, size_t len) { B43_WARN_ON(ring->tx); - dma_sync_single_for_device(ring->dev->dev->dma_dev, - addr, len, DMA_FROM_DEVICE); + ssb_dma_sync_single_for_device(ring->dev->dev, + addr, len, DMA_FROM_DEVICE); } static inline @@ -381,7 +381,6 @@ static inline static int alloc_ringmemory(struct b43_dmaring *ring) { - struct device *dma_dev = ring->dev->dev->dma_dev; gfp_t flags = GFP_KERNEL; /* The specs call for 4K buffers for 30- and 32-bit DMA with 4K @@ -392,11 +391,14 @@ static int alloc_ringmemory(struct b43_dmaring *ring) * For unknown reasons - possibly a hardware error - the BCM4311 rev * 02, which uses 64-bit DMA, needs the ring buffer in very low memory, * which accounts for the GFP_DMA flag below. + * + * The flags here must match the flags in free_ringmemory below! */ if (ring->type == B43_DMA_64BIT) flags |= GFP_DMA; - ring->descbase = dma_alloc_coherent(dma_dev, B43_DMA_RINGMEMSIZE, - &(ring->dmabase), flags); + ring->descbase = ssb_dma_alloc_consistent(ring->dev->dev, + B43_DMA_RINGMEMSIZE, + &(ring->dmabase), flags); if (!ring->descbase) { b43err(ring->dev->wl, "DMA ringmemory allocation failed\n"); return -ENOMEM; @@ -408,10 +410,13 @@ static int alloc_ringmemory(struct b43_dmaring *ring) static void free_ringmemory(struct b43_dmaring *ring) { - struct device *dma_dev = ring->dev->dev->dma_dev; + gfp_t flags = GFP_KERNEL; + + if (ring->type == B43_DMA_64BIT) + flags |= GFP_DMA; - dma_free_coherent(dma_dev, B43_DMA_RINGMEMSIZE, - ring->descbase, ring->dmabase); + ssb_dma_free_consistent(ring->dev->dev, B43_DMA_RINGMEMSIZE, + ring->descbase, ring->dmabase, flags); } /* Reset the RX DMA channel */ @@ -518,7 +523,7 @@ static bool b43_dma_mapping_error(struct b43_dmaring *ring, dma_addr_t addr, size_t buffersize, bool dma_to_device) { - if (unlikely(dma_mapping_error(addr))) + if (unlikely(ssb_dma_mapping_error(ring->dev->dev, addr))) return 1; switch (ring->type) { @@ -844,10 +849,10 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev, goto err_kfree_meta; /* test for ability to dma to txhdr_cache */ - dma_test = dma_map_single(dev->dev->dma_dev, - ring->txhdr_cache, - b43_txhdr_size(dev), - DMA_TO_DEVICE); + dma_test = ssb_dma_map_single(dev->dev, + ring->txhdr_cache, + b43_txhdr_size(dev), + DMA_TO_DEVICE); if (b43_dma_mapping_error(ring, dma_test, b43_txhdr_size(dev), 1)) { @@ -859,10 +864,10 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev, if (!ring->txhdr_cache) goto err_kfree_meta; - dma_test = dma_map_single(dev->dev->dma_dev, - ring->txhdr_cache, - b43_txhdr_size(dev), - DMA_TO_DEVICE); + dma_test = ssb_dma_map_single(dev->dev, + ring->txhdr_cache, + b43_txhdr_size(dev), + DMA_TO_DEVICE); if (b43_dma_mapping_error(ring, dma_test, b43_txhdr_size(dev), 1)) { @@ -873,9 +878,9 @@ struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev, } } - dma_unmap_single(dev->dev->dma_dev, - dma_test, b43_txhdr_size(dev), - DMA_TO_DEVICE); + ssb_dma_unmap_single(dev->dev, + dma_test, b43_txhdr_size(dev), + DMA_TO_DEVICE); } err = alloc_ringmemory(ring); diff --git a/drivers/net/wireless/b43legacy/dma.c b/drivers/net/wireless/b43legacy/dma.c index 33cc256c5baf..9736b2f56a75 100644 --- a/drivers/net/wireless/b43legacy/dma.c +++ b/drivers/net/wireless/b43legacy/dma.c @@ -393,13 +393,13 @@ dma_addr_t map_descbuffer(struct b43legacy_dmaring *ring, dma_addr_t dmaaddr; if (tx) - dmaaddr = dma_map_single(ring->dev->dev->dma_dev, - buf, len, - DMA_TO_DEVICE); + dmaaddr = ssb_dma_map_single(ring->dev->dev, + buf, len, + DMA_TO_DEVICE); else - dmaaddr = dma_map_single(ring->dev->dev->dma_dev, - buf, len, - DMA_FROM_DEVICE); + dmaaddr = ssb_dma_map_single(ring->dev->dev, + buf, len, + DMA_FROM_DEVICE); return dmaaddr; } @@ -411,13 +411,13 @@ void unmap_descbuffer(struct b43legacy_dmaring *ring, int tx) { if (tx) - dma_unmap_single(ring->dev->dev->dma_dev, - addr, len, - DMA_TO_DEVICE); + ssb_dma_unmap_single(ring->dev->dev, + addr, len, + DMA_TO_DEVICE); else - dma_unmap_single(ring->dev->dev->dma_dev, - addr, len, - DMA_FROM_DEVICE); + ssb_dma_unmap_single(ring->dev->dev, + addr, len, + DMA_FROM_DEVICE); } static inline @@ -427,8 +427,8 @@ void sync_descbuffer_for_cpu(struct b43legacy_dmaring *ring, { B43legacy_WARN_ON(ring->tx); - dma_sync_single_for_cpu(ring->dev->dev->dma_dev, - addr, len, DMA_FROM_DEVICE); + ssb_dma_sync_single_for_cpu(ring->dev->dev, + addr, len, DMA_FROM_DEVICE); } static inline @@ -438,8 +438,8 @@ void sync_descbuffer_for_device(struct b43legacy_dmaring *ring, { B43legacy_WARN_ON(ring->tx); - dma_sync_single_for_device(ring->dev->dev->dma_dev, - addr, len, DMA_FROM_DEVICE); + ssb_dma_sync_single_for_device(ring->dev->dev, + addr, len, DMA_FROM_DEVICE); } static inline @@ -458,10 +458,11 @@ void free_descriptor_buffer(struct b43legacy_dmaring *ring, static int alloc_ringmemory(struct b43legacy_dmaring *ring) { - struct device *dma_dev = ring->dev->dev->dma_dev; - - ring->descbase = dma_alloc_coherent(dma_dev, B43legacy_DMA_RINGMEMSIZE, - &(ring->dmabase), GFP_KERNEL); + /* GFP flags must match the flags in free_ringmemory()! */ + ring->descbase = ssb_dma_alloc_consistent(ring->dev->dev, + B43legacy_DMA_RINGMEMSIZE, + &(ring->dmabase), + GFP_KERNEL); if (!ring->descbase) { b43legacyerr(ring->dev->wl, "DMA ringmemory allocation" " failed\n"); @@ -474,10 +475,8 @@ static int alloc_ringmemory(struct b43legacy_dmaring *ring) static void free_ringmemory(struct b43legacy_dmaring *ring) { - struct device *dma_dev = ring->dev->dev->dma_dev; - - dma_free_coherent(dma_dev, B43legacy_DMA_RINGMEMSIZE, - ring->descbase, ring->dmabase); + ssb_dma_free_consistent(ring->dev->dev, B43legacy_DMA_RINGMEMSIZE, + ring->descbase, ring->dmabase, GFP_KERNEL); } /* Reset the RX DMA channel */ @@ -589,7 +588,7 @@ static bool b43legacy_dma_mapping_error(struct b43legacy_dmaring *ring, size_t buffersize, bool dma_to_device) { - if (unlikely(dma_mapping_error(addr))) + if (unlikely(ssb_dma_mapping_error(ring->dev->dev, addr))) return 1; switch (ring->type) { @@ -893,9 +892,9 @@ struct b43legacy_dmaring *b43legacy_setup_dmaring(struct b43legacy_wldev *dev, goto err_kfree_meta; /* test for ability to dma to txhdr_cache */ - dma_test = dma_map_single(dev->dev->dma_dev, ring->txhdr_cache, - sizeof(struct b43legacy_txhdr_fw3), - DMA_TO_DEVICE); + dma_test = ssb_dma_map_single(dev->dev, ring->txhdr_cache, + sizeof(struct b43legacy_txhdr_fw3), + DMA_TO_DEVICE); if (b43legacy_dma_mapping_error(ring, dma_test, sizeof(struct b43legacy_txhdr_fw3), 1)) { @@ -907,7 +906,7 @@ struct b43legacy_dmaring *b43legacy_setup_dmaring(struct b43legacy_wldev *dev, if (!ring->txhdr_cache) goto err_kfree_meta; - dma_test = dma_map_single(dev->dev->dma_dev, + dma_test = ssb_dma_map_single(dev->dev, ring->txhdr_cache, sizeof(struct b43legacy_txhdr_fw3), DMA_TO_DEVICE); @@ -917,9 +916,9 @@ struct b43legacy_dmaring *b43legacy_setup_dmaring(struct b43legacy_wldev *dev, goto err_kfree_txhdr_cache; } - dma_unmap_single(dev->dev->dma_dev, - dma_test, sizeof(struct b43legacy_txhdr_fw3), - DMA_TO_DEVICE); + ssb_dma_unmap_single(dev->dev, dma_test, + sizeof(struct b43legacy_txhdr_fw3), + DMA_TO_DEVICE); } ring->dev = dev; diff --git a/drivers/ssb/Kconfig b/drivers/ssb/Kconfig index cd845b8acd17..307b1f62d949 100644 --- a/drivers/ssb/Kconfig +++ b/drivers/ssb/Kconfig @@ -2,7 +2,7 @@ menu "Sonics Silicon Backplane" config SSB_POSSIBLE bool - depends on HAS_IOMEM + depends on HAS_IOMEM && HAS_DMA default y config SSB diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c index d184f2aea78d..d831a2beff39 100644 --- a/drivers/ssb/main.c +++ b/drivers/ssb/main.c @@ -462,18 +462,15 @@ static int ssb_devices_register(struct ssb_bus *bus) #ifdef CONFIG_SSB_PCIHOST sdev->irq = bus->host_pci->irq; dev->parent = &bus->host_pci->dev; - sdev->dma_dev = &bus->host_pci->dev; #endif break; case SSB_BUSTYPE_PCMCIA: #ifdef CONFIG_SSB_PCMCIAHOST sdev->irq = bus->host_pcmcia->irq.AssignedIRQ; dev->parent = &bus->host_pcmcia->dev; - sdev->dma_dev = &bus->host_pcmcia->dev; #endif break; case SSB_BUSTYPE_SSB: - sdev->dma_dev = dev; break; } @@ -1156,36 +1153,82 @@ u32 ssb_dma_translation(struct ssb_device *dev) { switch (dev->bus->bustype) { case SSB_BUSTYPE_SSB: - case SSB_BUSTYPE_PCMCIA: return 0; case SSB_BUSTYPE_PCI: return SSB_PCI_DMA; + default: + __ssb_dma_not_implemented(dev); } return 0; } EXPORT_SYMBOL(ssb_dma_translation); -int ssb_dma_set_mask(struct ssb_device *ssb_dev, u64 mask) +int ssb_dma_set_mask(struct ssb_device *dev, u64 mask) { - struct device *dma_dev = ssb_dev->dma_dev; - int err = 0; + int err; -#ifdef CONFIG_SSB_PCIHOST - if (ssb_dev->bus->bustype == SSB_BUSTYPE_PCI) { - err = pci_set_dma_mask(ssb_dev->bus->host_pci, mask); + switch (dev->bus->bustype) { + case SSB_BUSTYPE_PCI: + err = pci_set_dma_mask(dev->bus->host_pci, mask); if (err) return err; - err = pci_set_consistent_dma_mask(ssb_dev->bus->host_pci, mask); + err = pci_set_consistent_dma_mask(dev->bus->host_pci, mask); return err; + case SSB_BUSTYPE_SSB: + return dma_set_mask(dev->dev, mask); + default: + __ssb_dma_not_implemented(dev); } -#endif - dma_dev->coherent_dma_mask = mask; - dma_dev->dma_mask = &dma_dev->coherent_dma_mask; - - return err; + return -ENOSYS; } EXPORT_SYMBOL(ssb_dma_set_mask); +void * ssb_dma_alloc_consistent(struct ssb_device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t gfp_flags) +{ + switch (dev->bus->bustype) { + case SSB_BUSTYPE_PCI: + if (gfp_flags & GFP_DMA) { + /* Workaround: The PCI API does not support passing + * a GFP flag. */ + return dma_alloc_coherent(&dev->bus->host_pci->dev, + size, dma_handle, gfp_flags); + } + return pci_alloc_consistent(dev->bus->host_pci, size, dma_handle); + case SSB_BUSTYPE_SSB: + return dma_alloc_coherent(dev->dev, size, dma_handle, gfp_flags); + default: + __ssb_dma_not_implemented(dev); + } + return NULL; +} +EXPORT_SYMBOL(ssb_dma_alloc_consistent); + +void ssb_dma_free_consistent(struct ssb_device *dev, size_t size, + void *vaddr, dma_addr_t dma_handle, + gfp_t gfp_flags) +{ + switch (dev->bus->bustype) { + case SSB_BUSTYPE_PCI: + if (gfp_flags & GFP_DMA) { + /* Workaround: The PCI API does not support passing + * a GFP flag. */ + dma_free_coherent(&dev->bus->host_pci->dev, + size, vaddr, dma_handle); + return; + } + pci_free_consistent(dev->bus->host_pci, size, + vaddr, dma_handle); + return; + case SSB_BUSTYPE_SSB: + dma_free_coherent(dev->dev, size, vaddr, dma_handle); + return; + default: + __ssb_dma_not_implemented(dev); + } +} +EXPORT_SYMBOL(ssb_dma_free_consistent); + int ssb_bus_may_powerdown(struct ssb_bus *bus) { struct ssb_chipcommon *cc; diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h index 50dfd0dc4093..0fe5a0ded3ea 100644 --- a/include/linux/ssb/ssb.h +++ b/include/linux/ssb/ssb.h @@ -137,9 +137,6 @@ struct ssb_device { const struct ssb_bus_ops *ops; struct device *dev; - /* Pointer to the device that has to be used for - * any DMA related operation. */ - struct device *dma_dev; struct ssb_bus *bus; struct ssb_device_id id; @@ -399,13 +396,151 @@ static inline void ssb_block_write(struct ssb_device *dev, const void *buffer, #endif /* CONFIG_SSB_BLOCKIO */ +/* The SSB DMA API. Use this API for any DMA operation on the device. + * This API basically is a wrapper that calls the correct DMA API for + * the host device type the SSB device is attached to. */ + /* Translation (routing) bits that need to be ORed to DMA * addresses before they are given to a device. */ extern u32 ssb_dma_translation(struct ssb_device *dev); #define SSB_DMA_TRANSLATION_MASK 0xC0000000 #define SSB_DMA_TRANSLATION_SHIFT 30 -extern int ssb_dma_set_mask(struct ssb_device *ssb_dev, u64 mask); +extern int ssb_dma_set_mask(struct ssb_device *dev, u64 mask); + +extern void * ssb_dma_alloc_consistent(struct ssb_device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t gfp_flags); +extern void ssb_dma_free_consistent(struct ssb_device *dev, size_t size, + void *vaddr, dma_addr_t dma_handle, + gfp_t gfp_flags); + +static inline void __cold __ssb_dma_not_implemented(struct ssb_device *dev) +{ +#ifdef CONFIG_SSB_DEBUG + printk(KERN_ERR "SSB: BUG! Calling DMA API for " + "unsupported bustype %d\n", dev->bus->bustype); +#endif /* DEBUG */ +} + +static inline int ssb_dma_mapping_error(struct ssb_device *dev, dma_addr_t addr) +{ + switch (dev->bus->bustype) { + case SSB_BUSTYPE_PCI: + return pci_dma_mapping_error(addr); + case SSB_BUSTYPE_SSB: + return dma_mapping_error(addr); + default: + __ssb_dma_not_implemented(dev); + } + return -ENOSYS; +} + +static inline dma_addr_t ssb_dma_map_single(struct ssb_device *dev, void *p, + size_t size, enum dma_data_direction dir) +{ + switch (dev->bus->bustype) { + case SSB_BUSTYPE_PCI: + return pci_map_single(dev->bus->host_pci, p, size, dir); + case SSB_BUSTYPE_SSB: + return dma_map_single(dev->dev, p, size, dir); + default: + __ssb_dma_not_implemented(dev); + } + return 0; +} + +static inline void ssb_dma_unmap_single(struct ssb_device *dev, dma_addr_t dma_addr, + size_t size, enum dma_data_direction dir) +{ + switch (dev->bus->bustype) { + case SSB_BUSTYPE_PCI: + pci_unmap_single(dev->bus->host_pci, dma_addr, size, dir); + return; + case SSB_BUSTYPE_SSB: + dma_unmap_single(dev->dev, dma_addr, size, dir); + return; + default: + __ssb_dma_not_implemented(dev); + } +} + +static inline void ssb_dma_sync_single_for_cpu(struct ssb_device *dev, + dma_addr_t dma_addr, + size_t size, + enum dma_data_direction dir) +{ + switch (dev->bus->bustype) { + case SSB_BUSTYPE_PCI: + pci_dma_sync_single_for_cpu(dev->bus->host_pci, dma_addr, + size, dir); + return; + case SSB_BUSTYPE_SSB: + dma_sync_single_for_cpu(dev->dev, dma_addr, size, dir); + return; + default: + __ssb_dma_not_implemented(dev); + } +} + +static inline void ssb_dma_sync_single_for_device(struct ssb_device *dev, + dma_addr_t dma_addr, + size_t size, + enum dma_data_direction dir) +{ + switch (dev->bus->bustype) { + case SSB_BUSTYPE_PCI: + pci_dma_sync_single_for_device(dev->bus->host_pci, dma_addr, + size, dir); + return; + case SSB_BUSTYPE_SSB: + dma_sync_single_for_device(dev->dev, dma_addr, size, dir); + return; + default: + __ssb_dma_not_implemented(dev); + } +} + +static inline void ssb_dma_sync_single_range_for_cpu(struct ssb_device *dev, + dma_addr_t dma_addr, + unsigned long offset, + size_t size, + enum dma_data_direction dir) +{ + switch (dev->bus->bustype) { + case SSB_BUSTYPE_PCI: + /* Just sync everything. That's all the PCI API can do. */ + pci_dma_sync_single_for_cpu(dev->bus->host_pci, dma_addr, + offset + size, dir); + return; + case SSB_BUSTYPE_SSB: + dma_sync_single_range_for_cpu(dev->dev, dma_addr, offset, + size, dir); + return; + default: + __ssb_dma_not_implemented(dev); + } +} + +static inline void ssb_dma_sync_single_range_for_device(struct ssb_device *dev, + dma_addr_t dma_addr, + unsigned long offset, + size_t size, + enum dma_data_direction dir) +{ + switch (dev->bus->bustype) { + case SSB_BUSTYPE_PCI: + /* Just sync everything. That's all the PCI API can do. */ + pci_dma_sync_single_for_device(dev->bus->host_pci, dma_addr, + offset + size, dir); + return; + case SSB_BUSTYPE_SSB: + dma_sync_single_range_for_device(dev->dev, dma_addr, offset, + size, dir); + return; + default: + __ssb_dma_not_implemented(dev); + } +} #ifdef CONFIG_SSB_PCIHOST -- cgit v1.2.3 From ffd7891dc909b3648e87f7cf8f84a6dc12fc1cc6 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Sat, 21 Jun 2008 10:02:46 -0400 Subject: mac80211: Let drivers have access to TKIP key offets for TX and RX MIC Some drivers may want to to use the TKIP key offsets for TX and RX MIC so lets move this out. Lets also clear up a bit how this is used internally in mac80211. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- include/linux/nl80211.h | 5 ++++- include/net/mac80211.h | 7 ++++++- net/mac80211/key.h | 37 ++++++++++++------------------------- net/mac80211/tkip.c | 10 +++++----- net/mac80211/wpa.c | 20 ++++++++++++++------ 5 files changed, 41 insertions(+), 38 deletions(-) (limited to 'include/linux') diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index aa8411e2a160..2be7c63bc0f2 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -241,7 +241,10 @@ enum nl80211_attrs { NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1 }; -#define NL80211_MAX_SUPP_RATES 32 +#define NL80211_MAX_SUPP_RATES 32 +#define NL80211_TKIP_DATA_OFFSET_ENCR_KEY 0 +#define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY 16 +#define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY 24 /** * enum nl80211_iftype - (virtual) interface types diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 7ab4ff6159a2..19f1e412a0f0 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -595,7 +595,12 @@ enum ieee80211_key_flags { * @flags: key flags, see &enum ieee80211_key_flags. * @keyidx: the key index (0-3) * @keylen: key material length - * @key: key material + * @key: key material. For ALG_TKIP the key is encoded as a 256-bit (32 byte) + * data block: + * - Temporal Encryption Key (128 bits) + * - Temporal Authenticator Tx MIC Key (64 bits) + * - Temporal Authenticator Rx MIC Key (64 bits) + * */ struct ieee80211_key_conf { enum ieee80211_key_alg alg; diff --git a/net/mac80211/key.h b/net/mac80211/key.h index a0f774aafa45..425816e0996c 100644 --- a/net/mac80211/key.h +++ b/net/mac80211/key.h @@ -16,31 +16,18 @@ #include #include -/* ALG_TKIP - * struct ieee80211_key::key is encoded as a 256-bit (32 byte) data block: - * Temporal Encryption Key (128 bits) - * Temporal Authenticator Tx MIC Key (64 bits) - * Temporal Authenticator Rx MIC Key (64 bits) - */ - -#define WEP_IV_LEN 4 -#define WEP_ICV_LEN 4 - -#define ALG_TKIP_KEY_LEN 32 -/* Starting offsets for each key */ -#define ALG_TKIP_TEMP_ENCR_KEY 0 -#define ALG_TKIP_TEMP_AUTH_TX_MIC_KEY 16 -#define ALG_TKIP_TEMP_AUTH_RX_MIC_KEY 24 -#define TKIP_IV_LEN 8 -#define TKIP_ICV_LEN 4 - -#define ALG_CCMP_KEY_LEN 16 -#define CCMP_HDR_LEN 8 -#define CCMP_MIC_LEN 8 -#define CCMP_TK_LEN 16 -#define CCMP_PN_LEN 6 - -#define NUM_RX_DATA_QUEUES 17 +#define WEP_IV_LEN 4 +#define WEP_ICV_LEN 4 +#define ALG_TKIP_KEY_LEN 32 +#define ALG_CCMP_KEY_LEN 16 +#define CCMP_HDR_LEN 8 +#define CCMP_MIC_LEN 8 +#define CCMP_TK_LEN 16 +#define CCMP_PN_LEN 6 +#define TKIP_IV_LEN 8 +#define TKIP_ICV_LEN 4 + +#define NUM_RX_DATA_QUEUES 17 struct ieee80211_local; struct ieee80211_sub_if_data; diff --git a/net/mac80211/tkip.c b/net/mac80211/tkip.c index 699807889988..995f7af3d25e 100644 --- a/net/mac80211/tkip.c +++ b/net/mac80211/tkip.c @@ -164,7 +164,7 @@ void ieee80211_get_tkip_key(struct ieee80211_key_conf *keyconf, iv16 = data[2] | (data[0] << 8); iv32 = get_unaligned_le32(&data[4]); - tk = &key->conf.key[ALG_TKIP_TEMP_ENCR_KEY]; + tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY]; ctx = &key->u.tkip.tx; #ifdef CONFIG_MAC80211_TKIP_DEBUG @@ -205,7 +205,7 @@ void ieee80211_tkip_encrypt_data(struct crypto_blkcipher *tfm, { u8 rc4key[16]; struct tkip_ctx *ctx = &key->u.tkip.tx; - const u8 *tk = &key->conf.key[ALG_TKIP_TEMP_ENCR_KEY]; + const u8 *tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY]; /* Calculate per-packet key */ if (ctx->iv16 == 0 || !ctx->initialized) @@ -231,7 +231,7 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm, u32 iv16; u8 rc4key[16], keyid, *pos = payload; int res; - const u8 *tk = &key->conf.key[ALG_TKIP_TEMP_ENCR_KEY]; + const u8 *tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY]; if (payload_len < 12) return -1; @@ -286,13 +286,13 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm, #ifdef CONFIG_MAC80211_TKIP_DEBUG { int i; + u8 key_offset = NL80211_TKIP_DATA_OFFSET_ENCR_KEY; DECLARE_MAC_BUF(mac); printk(KERN_DEBUG "TKIP decrypt: Phase1 TA=%s" " TK=", print_mac(mac, ta)); for (i = 0; i < 16; i++) printk("%02x ", - key->conf.key[ - ALG_TKIP_TEMP_ENCR_KEY + i]); + key->conf.key[key_offset + i]); printk("\n"); printk(KERN_DEBUG "TKIP decrypt: P1K="); for (i = 0; i < 5; i++) diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index 345e10e9b313..f809761fbfb5 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -49,7 +49,7 @@ static int ieee80211_get_hdr_info(const struct sk_buff *skb, u8 **sa, u8 **da, ieee80211_tx_result ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx) { - u8 *data, *sa, *da, *key, *mic, qos_tid; + u8 *data, *sa, *da, *key, *mic, qos_tid, key_offset; size_t data_len; u16 fc; struct sk_buff *skb = tx->skb; @@ -88,8 +88,12 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx) #else authenticator = 1; #endif - key = &tx->key->conf.key[authenticator ? ALG_TKIP_TEMP_AUTH_TX_MIC_KEY : - ALG_TKIP_TEMP_AUTH_RX_MIC_KEY]; + /* At this point we know we're using ALG_TKIP. To get the MIC key + * we now will rely on the offset from the ieee80211_key_conf::key */ + key_offset = authenticator ? + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY : + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY; + key = &tx->key->conf.key[key_offset]; mic = skb_put(skb, MICHAEL_MIC_LEN); michael_mic(key, da, sa, qos_tid & 0x0f, data, data_len, mic); @@ -100,7 +104,7 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx) ieee80211_rx_result ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx) { - u8 *data, *sa, *da, *key = NULL, qos_tid; + u8 *data, *sa, *da, *key = NULL, qos_tid, key_offset; size_t data_len; u16 fc; u8 mic[MICHAEL_MIC_LEN]; @@ -131,8 +135,12 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx) #else authenticator = 1; #endif - key = &rx->key->conf.key[authenticator ? ALG_TKIP_TEMP_AUTH_RX_MIC_KEY : - ALG_TKIP_TEMP_AUTH_TX_MIC_KEY]; + /* At this point we know we're using ALG_TKIP. To get the MIC key + * we now will rely on the offset from the ieee80211_key_conf::key */ + key_offset = authenticator ? + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY : + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY; + key = &rx->key->conf.key[key_offset]; michael_mic(key, da, sa, qos_tid & 0x0f, data, data_len, mic); if (memcmp(mic, data + data_len, MICHAEL_MIC_LEN) != 0 || wpa_test) { if (!(rx->flags & IEEE80211_RX_RA_MATCH)) -- cgit v1.2.3 From 9433f6dd3a4677e9b92c6e1cd7f98b11598b7c2c Mon Sep 17 00:00:00 2001 From: Wang Chen Date: Thu, 26 Jun 2008 10:50:04 +0800 Subject: PCI: Fix comment of pci_dynids struct pci_driver has no field of driver_data. It's in pci_device_id. Signed-off-by: Wang Chen Signed-off-by: Jesse Barnes --- include/linux/pci.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/pci.h b/include/linux/pci.h index f1f73f79a180..76c9a4a96152 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -338,7 +338,7 @@ struct pci_bus_region { struct pci_dynids { spinlock_t lock; /* protects list, index */ struct list_head list; /* for IDs added at runtime */ - unsigned int use_driver_data:1; /* pci_driver->driver_data is used */ + unsigned int use_driver_data:1; /* pci_device_id->driver_data is used */ }; /* ---------------------------------------------------------------- */ -- cgit v1.2.3 From a0da84f35b25875870270d16b6eccda4884d61a7 Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Sat, 28 Jun 2008 08:31:22 +1000 Subject: Improve setting of "events_cleared" for write-intent bitmaps. When an array is degraded, bits in the write-intent bitmap are not cleared, so that if the missing device is re-added, it can be synced by only updated those parts of the device that have changed since it was removed. The enable this a 'events_cleared' value is stored. It is the event counter for the array the last time that any bits were cleared. Sometimes - if a device disappears from an array while it is 'clean' - the events_cleared value gets updated incorrectly (there are subtle ordering issues between updateing events in the main metadata and the bitmap metadata) resulting in the missing device appearing to require a full resync when it is re-added. With this patch, we update events_cleared precisely when we are about to clear a bit in the bitmap. We record events_cleared when we clear the bit internally, and copy that to the superblock which is written out before the bit on storage. This makes it more "obviously correct". We also need to update events_cleared when the event_count is going backwards (as happens on a dirty->clean transition of a non-degraded array). Thanks to Mike Snitzer for identifying this problem and testing early "fixes". Cc: "Mike Snitzer" Signed-off-by: Neil Brown --- drivers/md/bitmap.c | 29 ++++++++++++++++++++++++----- include/linux/raid/bitmap.h | 1 + 2 files changed, 25 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c index b26927ce889c..dedba16d42f7 100644 --- a/drivers/md/bitmap.c +++ b/drivers/md/bitmap.c @@ -454,8 +454,11 @@ void bitmap_update_sb(struct bitmap *bitmap) spin_unlock_irqrestore(&bitmap->lock, flags); sb = (bitmap_super_t *)kmap_atomic(bitmap->sb_page, KM_USER0); sb->events = cpu_to_le64(bitmap->mddev->events); - if (!bitmap->mddev->degraded) - sb->events_cleared = cpu_to_le64(bitmap->mddev->events); + if (bitmap->mddev->events < bitmap->events_cleared) { + /* rocking back to read-only */ + bitmap->events_cleared = bitmap->mddev->events; + sb->events_cleared = cpu_to_le64(bitmap->events_cleared); + } kunmap_atomic(sb, KM_USER0); write_page(bitmap, bitmap->sb_page, 1); } @@ -1085,9 +1088,19 @@ void bitmap_daemon_work(struct bitmap *bitmap) } else spin_unlock_irqrestore(&bitmap->lock, flags); lastpage = page; -/* - printk("bitmap clean at page %lu\n", j); -*/ + + /* We are possibly going to clear some bits, so make + * sure that events_cleared is up-to-date. + */ + if (bitmap->need_sync) { + bitmap_super_t *sb; + bitmap->need_sync = 0; + sb = kmap_atomic(bitmap->sb_page, KM_USER0); + sb->events_cleared = + cpu_to_le64(bitmap->events_cleared); + kunmap_atomic(sb, KM_USER0); + write_page(bitmap, bitmap->sb_page, 1); + } spin_lock_irqsave(&bitmap->lock, flags); clear_page_attr(bitmap, page, BITMAP_PAGE_CLEAN); } @@ -1257,6 +1270,12 @@ void bitmap_endwrite(struct bitmap *bitmap, sector_t offset, unsigned long secto return; } + if (success && + bitmap->events_cleared < bitmap->mddev->events) { + bitmap->events_cleared = bitmap->mddev->events; + bitmap->need_sync = 1; + } + if (!success && ! (*bmc & NEEDED_MASK)) *bmc |= NEEDED_MASK; diff --git a/include/linux/raid/bitmap.h b/include/linux/raid/bitmap.h index 78bfdea24a8e..e98900671ca9 100644 --- a/include/linux/raid/bitmap.h +++ b/include/linux/raid/bitmap.h @@ -221,6 +221,7 @@ struct bitmap { unsigned long syncchunk; __u64 events_cleared; + int need_sync; /* bitmap spinlock */ spinlock_t lock; -- cgit v1.2.3 From 5e96ee65c8bd629ce093da67a066d3946468298a Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Sat, 28 Jun 2008 08:31:24 +1000 Subject: Allow setting start point for requested check/repair This makes it possible to just resync a small part of an array. e.g. if a drive reports that it has questionable sectors, a 'repair' of just the region covering those sectors will cause them to be read and, if there is an error, re-written with correct data. Signed-off-by: Neil Brown --- drivers/md/md.c | 47 ++++++++++++++++++++++++++++++++++++++++++----- include/linux/raid/md_k.h | 2 ++ 2 files changed, 44 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/md.c b/drivers/md/md.c index 2580ac1b9b0f..261322722c19 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -278,6 +278,7 @@ static mddev_t * mddev_find(dev_t unit) init_waitqueue_head(&new->sb_wait); init_waitqueue_head(&new->recovery_wait); new->reshape_position = MaxSector; + new->resync_min = 0; new->resync_max = MaxSector; new->level = LEVEL_NONE; @@ -3074,6 +3075,36 @@ sync_completed_show(mddev_t *mddev, char *page) static struct md_sysfs_entry md_sync_completed = __ATTR_RO(sync_completed); +static ssize_t +min_sync_show(mddev_t *mddev, char *page) +{ + return sprintf(page, "%llu\n", + (unsigned long long)mddev->resync_min); +} +static ssize_t +min_sync_store(mddev_t *mddev, const char *buf, size_t len) +{ + unsigned long long min; + if (strict_strtoull(buf, 10, &min)) + return -EINVAL; + if (min > mddev->resync_max) + return -EINVAL; + if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery)) + return -EBUSY; + + /* Must be a multiple of chunk_size */ + if (mddev->chunk_size) { + if (min & (sector_t)((mddev->chunk_size>>9)-1)) + return -EINVAL; + } + mddev->resync_min = min; + + return len; +} + +static struct md_sysfs_entry md_min_sync = +__ATTR(sync_min, S_IRUGO|S_IWUSR, min_sync_show, min_sync_store); + static ssize_t max_sync_show(mddev_t *mddev, char *page) { @@ -3089,9 +3120,10 @@ max_sync_store(mddev_t *mddev, const char *buf, size_t len) if (strncmp(buf, "max", 3) == 0) mddev->resync_max = MaxSector; else { - char *ep; - unsigned long long max = simple_strtoull(buf, &ep, 10); - if (ep == buf || (*ep != 0 && *ep != '\n')) + unsigned long long max; + if (strict_strtoull(buf, 10, &max)) + return -EINVAL; + if (max < mddev->resync_min) return -EINVAL; if (max < mddev->resync_max && test_bit(MD_RECOVERY_RUNNING, &mddev->recovery)) @@ -3222,6 +3254,7 @@ static struct attribute *md_redundancy_attrs[] = { &md_sync_speed.attr, &md_sync_force_parallel.attr, &md_sync_completed.attr, + &md_min_sync.attr, &md_max_sync.attr, &md_suspend_lo.attr, &md_suspend_hi.attr, @@ -3777,6 +3810,7 @@ static int do_md_stop(mddev_t * mddev, int mode) mddev->size = 0; mddev->raid_disks = 0; mddev->recovery_cp = 0; + mddev->resync_min = 0; mddev->resync_max = MaxSector; mddev->reshape_position = MaxSector; mddev->external = 0; @@ -5625,9 +5659,11 @@ void md_do_sync(mddev_t *mddev) max_sectors = mddev->resync_max_sectors; mddev->resync_mismatches = 0; /* we don't use the checkpoint if there's a bitmap */ - if (!mddev->bitmap && - !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) + if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) + j = mddev->resync_min; + else if (!mddev->bitmap) j = mddev->recovery_cp; + } else if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery)) max_sectors = mddev->size << 1; else { @@ -5796,6 +5832,7 @@ void md_do_sync(mddev_t *mddev) skip: mddev->curr_resync = 0; + mddev->resync_min = 0; mddev->resync_max = MaxSector; sysfs_notify(&mddev->kobj, NULL, "sync_completed"); wake_up(&resync_wait); diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index 3dea9f545c8f..780e0613e6d5 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -227,6 +227,8 @@ struct mddev_s atomic_t recovery_active; /* blocks scheduled, but not written */ wait_queue_head_t recovery_wait; sector_t recovery_cp; + sector_t resync_min; /* user requested sync + * starts here */ sector_t resync_max; /* resync should pause * when it gets here */ -- cgit v1.2.3 From 72a23c211e4587859d5bf61ac4962d76e593fb02 Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Sat, 28 Jun 2008 08:31:41 +1000 Subject: Make sure all changes to md/sync_action are notified. When the 'resync' thread starts or stops, when we explicitly set sync_action, or when we determine that there is definitely nothing to do, we notify sync_action. To stop "sync_action" from occasionally showing the wrong value, we introduce a new flags - MD_RECOVERY_RECOVER - to say that a recovery is probably needed or happening, and we make sure that we set MD_RECOVERY_RUNNING before clearing MD_RECOVERY_NEEDED. Signed-off-by: Neil Brown --- Documentation/md.txt | 6 ++++++ drivers/md/md.c | 34 ++++++++++++++++++++++++++++------ include/linux/raid/md_k.h | 2 ++ 3 files changed, 36 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/Documentation/md.txt b/Documentation/md.txt index dca97ba4944a..c05bfb55659e 100644 --- a/Documentation/md.txt +++ b/Documentation/md.txt @@ -386,6 +386,12 @@ also have 'check' and 'repair' will start the appropriate process providing the current state is 'idle'. + This file responds to select/poll. Any important change in the value + triggers a poll event. Sometimes the value will briefly be + "recover" if a recovery seems to be needed, but cannot be + achieved. In that case, the transition to "recover" isn't + notified, but the transition away is. + mismatch_count When performing 'check' and 'repair', and possibly when performing 'resync', md will count the number of errors that are diff --git a/drivers/md/md.c b/drivers/md/md.c index 5b9d4fe4e6e4..c26dcad8a3ac 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -169,7 +169,6 @@ void md_new_event(mddev_t *mddev) { atomic_inc(&md_event_count); wake_up(&md_event_waiters); - sysfs_notify(&mddev->kobj, NULL, "sync_action"); } EXPORT_SYMBOL_GPL(md_new_event); @@ -2936,7 +2935,7 @@ action_show(mddev_t *mddev, char *page) type = "check"; else type = "repair"; - } else + } else if (test_bit(MD_RECOVERY_RECOVER, &mddev->recovery)) type = "recover"; } return sprintf(page, "%s\n", type); @@ -2958,9 +2957,12 @@ action_store(mddev_t *mddev, const char *page, size_t len) } else if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) || test_bit(MD_RECOVERY_NEEDED, &mddev->recovery)) return -EBUSY; - else if (cmd_match(page, "resync") || cmd_match(page, "recover")) + else if (cmd_match(page, "resync")) + set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + else if (cmd_match(page, "recover")) { + set_bit(MD_RECOVERY_RECOVER, &mddev->recovery); set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); - else if (cmd_match(page, "reshape")) { + } else if (cmd_match(page, "reshape")) { int err; if (mddev->pers->start_reshape == NULL) return -EINVAL; @@ -2977,6 +2979,7 @@ action_store(mddev_t *mddev, const char *page, size_t len) } set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); md_wakeup_thread(mddev->thread); + sysfs_notify(&mddev->kobj, NULL, "sync_action"); return len; } @@ -3682,6 +3685,7 @@ static int do_md_run(mddev_t * mddev) mddev->changed = 1; md_new_event(mddev); sysfs_notify(&mddev->kobj, NULL, "array_state"); + sysfs_notify(&mddev->kobj, NULL, "sync_action"); kobject_uevent(&mddev->gendisk->dev.kobj, KOBJ_CHANGE); return 0; } @@ -4252,6 +4256,8 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info) export_rdev(rdev); md_update_sb(mddev, 1); + if (mddev->degraded) + set_bit(MD_RECOVERY_RECOVER, &mddev->recovery); set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); md_wakeup_thread(mddev->thread); return err; @@ -5105,6 +5111,8 @@ void md_error(mddev_t *mddev, mdk_rdev_t *rdev) if (!mddev->pers->error_handler) return; mddev->pers->error_handler(mddev,rdev); + if (mddev->degraded) + set_bit(MD_RECOVERY_RECOVER, &mddev->recovery); set_bit(MD_RECOVERY_INTR, &mddev->recovery); set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); md_wakeup_thread(mddev->thread); @@ -6055,13 +6063,18 @@ void md_check_recovery(mddev_t *mddev) mddev->recovery = 0; /* flag recovery needed just to double check */ set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + sysfs_notify(&mddev->kobj, NULL, "sync_action"); md_new_event(mddev); goto unlock; } + /* Set RUNNING before clearing NEEDED to avoid + * any transients in the value of "sync_action". + */ + set_bit(MD_RECOVERY_RUNNING, &mddev->recovery); + clear_bit(MD_RECOVERY_NEEDED, &mddev->recovery); /* Clear some bits that don't mean anything, but * might be left set */ - clear_bit(MD_RECOVERY_NEEDED, &mddev->recovery); clear_bit(MD_RECOVERY_INTR, &mddev->recovery); clear_bit(MD_RECOVERY_DONE, &mddev->recovery); @@ -6079,17 +6092,19 @@ void md_check_recovery(mddev_t *mddev) /* Cannot proceed */ goto unlock; set_bit(MD_RECOVERY_RESHAPE, &mddev->recovery); + clear_bit(MD_RECOVERY_RECOVER, &mddev->recovery); } else if ((spares = remove_and_add_spares(mddev))) { clear_bit(MD_RECOVERY_SYNC, &mddev->recovery); clear_bit(MD_RECOVERY_CHECK, &mddev->recovery); + set_bit(MD_RECOVERY_RECOVER, &mddev->recovery); } else if (mddev->recovery_cp < MaxSector) { set_bit(MD_RECOVERY_SYNC, &mddev->recovery); + clear_bit(MD_RECOVERY_RECOVER, &mddev->recovery); } else if (!test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) /* nothing to be done ... */ goto unlock; if (mddev->pers->sync_request) { - set_bit(MD_RECOVERY_RUNNING, &mddev->recovery); if (spares && mddev->bitmap && ! mddev->bitmap->file) { /* We are adding a device or devices to an array * which has the bitmap stored on all devices. @@ -6108,9 +6123,16 @@ void md_check_recovery(mddev_t *mddev) mddev->recovery = 0; } else md_wakeup_thread(mddev->sync_thread); + sysfs_notify(&mddev->kobj, NULL, "sync_action"); md_new_event(mddev); } unlock: + if (!mddev->sync_thread) { + clear_bit(MD_RECOVERY_RUNNING, &mddev->recovery); + if (test_and_clear_bit(MD_RECOVERY_RECOVER, + &mddev->recovery)) + sysfs_notify(&mddev->kobj, NULL, "sync_action"); + } mddev_unlock(mddev); } } diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index 780e0613e6d5..62aa9c9a6ddc 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -188,6 +188,7 @@ struct mddev_s * NEEDED: we might need to start a resync/recover * RUNNING: a thread is running, or about to be started * SYNC: actually doing a resync, not a recovery + * RECOVER: doing recovery, or need to try it. * INTR: resync needs to be aborted for some reason * DONE: thread is done and is waiting to be reaped * REQUEST: user-space has requested a sync (used with SYNC) @@ -198,6 +199,7 @@ struct mddev_s */ #define MD_RECOVERY_RUNNING 0 #define MD_RECOVERY_SYNC 1 +#define MD_RECOVERY_RECOVER 2 #define MD_RECOVERY_INTR 3 #define MD_RECOVERY_DONE 4 #define MD_RECOVERY_NEEDED 5 -- cgit v1.2.3 From 526647320e696f434647f38421a6ecf65b859c43 Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Sat, 28 Jun 2008 08:31:44 +1000 Subject: Make sure all changes to md/dev-XX/state are notified The important state change happens during an interrupt in md_error. So just set a flag there and call sysfs_notify later in process context. Signed-off-by: Neil Brown --- Documentation/md.txt | 10 ++++++++++ drivers/md/md.c | 14 +++++++++++++- include/linux/raid/md_k.h | 3 +++ 3 files changed, 26 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/Documentation/md.txt b/Documentation/md.txt index eb6e69e3732e..e06cc59437e4 100644 --- a/Documentation/md.txt +++ b/Documentation/md.txt @@ -297,6 +297,10 @@ Each directory contains: writemostly - device will only be subject to read requests if there are no other options. This applies only to raid1 arrays. + blocked - device has failed, metadata is "external", + and the failure hasn't been acknowledged yet. + Writes that would write to this device if + it were not faulty are blocked. spare - device is working, but not a full member. This includes spares that are in the process of being recovered to @@ -306,6 +310,12 @@ Each directory contains: Writing "remove" removes the device from the array. Writing "writemostly" sets the writemostly flag. Writing "-writemostly" clears the writemostly flag. + Writing "blocked" sets the "blocked" flag. + Writing "-blocked" clear the "blocked" flag and allows writes + to complete. + + This file responds to select/poll. Any change to 'faulty' + or 'blocked' causes an event. errors An approximate count of read errors that have been detected on diff --git a/drivers/md/md.c b/drivers/md/md.c index 60d4cad88c20..dc99d95a1b6d 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -1886,6 +1886,8 @@ state_store(mdk_rdev_t *rdev, const char *buf, size_t len) err = 0; } + if (!err) + sysfs_notify(&rdev->kobj, NULL, "state"); return err ? err : len; } static struct rdev_sysfs_entry rdev_state = @@ -1979,7 +1981,8 @@ slot_store(mdk_rdev_t *rdev, const char *buf, size_t len) if (err) { rdev->raid_disk = -1; return err; - } + } else + sysfs_notify(&rdev->kobj, NULL, "state"); sprintf(nm, "rd%d", rdev->raid_disk); if (sysfs_create_link(&rdev->mddev->kobj, &rdev->kobj, nm)) printk(KERN_WARNING @@ -1996,6 +1999,7 @@ slot_store(mdk_rdev_t *rdev, const char *buf, size_t len) clear_bit(Faulty, &rdev->flags); clear_bit(WriteMostly, &rdev->flags); set_bit(In_sync, &rdev->flags); + sysfs_notify(&rdev->kobj, NULL, "state"); } return len; } @@ -3525,6 +3529,7 @@ static int do_md_run(mddev_t * mddev) return -EINVAL; } } + sysfs_notify(&rdev->kobj, NULL, "state"); } md_probe(mddev->unit, NULL, NULL); @@ -4256,6 +4261,8 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info) } if (err) export_rdev(rdev); + else + sysfs_notify(&rdev->kobj, NULL, "state"); md_update_sb(mddev, 1); if (mddev->degraded) @@ -5115,6 +5122,7 @@ void md_error(mddev_t *mddev, mdk_rdev_t *rdev) mddev->pers->error_handler(mddev,rdev); if (mddev->degraded) set_bit(MD_RECOVERY_RECOVER, &mddev->recovery); + set_bit(StateChanged, &rdev->flags); set_bit(MD_RECOVERY_INTR, &mddev->recovery); set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); md_wakeup_thread(mddev->thread); @@ -6037,6 +6045,10 @@ void md_check_recovery(mddev_t *mddev) if (mddev->flags) md_update_sb(mddev, 0); + rdev_for_each(rdev, rtmp, mddev) + if (test_and_clear_bit(StateChanged, &rdev->flags)) + sysfs_notify(&rdev->kobj, NULL, "state"); + if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) && !test_bit(MD_RECOVERY_DONE, &mddev->recovery)) { diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index 62aa9c9a6ddc..df30c4395875 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -87,6 +87,9 @@ struct mdk_rdev_s #define Blocked 8 /* An error occured on an externally * managed array, don't allow writes * until it is cleared */ +#define StateChanged 9 /* Faulty or Blocked has changed during + * interrupt, so it needs to be + * notified by the thread */ wait_queue_head_t blocked_wait; int desc_nr; /* descriptor index in the superblock */ -- cgit v1.2.3 From b203886edbcaac3ca427cf4dbcb50b18bdb346fd Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sat, 28 Jun 2008 08:31:50 +1000 Subject: md: kill STRIPE_OP_MOD_DMA in raid5 offload From: Dan Williams This micro-optimization allowed the raid code to skip a re-read of the parity block after checking parity. It took advantage of the fact that xor-offload-engines have their own internal result buffer and can check parity without writing to memory. Remove it for the following reasons: 1/ It is a layering violation for MD to need to manage the DMA and non-DMA paths within async_xor_zero_sum 2/ Bad precedent to toggle the 'ops' flags outside the lock 3/ Hard to realize a performance gain as reads will not need an updated parity block and writes will dirty it anyways. Signed-off-by: Dan Williams Signed-off-by: Neil Brown --- drivers/md/raid5.c | 10 ---------- include/linux/raid/raid5.h | 2 -- 2 files changed, 12 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 8c4e6149daea..60e61d2464b5 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -837,15 +837,10 @@ ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, static void ops_complete_check(void *stripe_head_ref) { struct stripe_head *sh = stripe_head_ref; - int pd_idx = sh->pd_idx; pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); - if (test_and_clear_bit(STRIPE_OP_MOD_DMA_CHECK, &sh->ops.pending) && - sh->ops.zero_sum_result == 0) - set_bit(R5_UPTODATE, &sh->dev[pd_idx].flags); - set_bit(STRIPE_OP_CHECK, &sh->ops.complete); set_bit(STRIPE_HANDLE, &sh->state); release_stripe(sh); @@ -873,11 +868,6 @@ static void ops_run_check(struct stripe_head *sh) tx = async_xor_zero_sum(xor_dest, xor_srcs, 0, count, STRIPE_SIZE, &sh->ops.zero_sum_result, 0, NULL, NULL, NULL); - if (tx) - set_bit(STRIPE_OP_MOD_DMA_CHECK, &sh->ops.pending); - else - clear_bit(STRIPE_OP_MOD_DMA_CHECK, &sh->ops.pending); - atomic_inc(&sh->count); tx = async_trigger_callback(ASYNC_TX_DEP_ACK | ASYNC_TX_ACK, tx, ops_complete_check, sh); diff --git a/include/linux/raid/raid5.h b/include/linux/raid/raid5.h index f0827d31ae6f..4ecae31a3dcb 100644 --- a/include/linux/raid/raid5.h +++ b/include/linux/raid/raid5.h @@ -267,10 +267,8 @@ struct r6_state { /* modifiers to the base operations * STRIPE_OP_MOD_REPAIR_PD - compute the parity block and write it back - * STRIPE_OP_MOD_DMA_CHECK - parity is not corrupted by the check */ #define STRIPE_OP_MOD_REPAIR_PD 7 -#define STRIPE_OP_MOD_DMA_CHECK 8 /* * Plugging: -- cgit v1.2.3 From 2b7497f0e0a0b9cf21d822e427d5399b2056501a Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sat, 28 Jun 2008 08:31:52 +1000 Subject: md: kill STRIPE_OP_IO flag From: Dan Williams The R5_Want{Read,Write} flags already gate i/o. So, this flag is superfluous and we can unconditionally call ops_run_io(). Signed-off-by: Dan Williams Signed-off-by: Neil Brown --- drivers/md/raid5.c | 32 +++++--------------------------- include/linux/raid/raid5.h | 1 - 2 files changed, 5 insertions(+), 28 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 60e61d2464b5..cac97080b278 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -373,8 +373,6 @@ static unsigned long get_stripe_work(struct stripe_head *sh) test_and_ack_op(STRIPE_OP_BIODRAIN, pending); test_and_ack_op(STRIPE_OP_POSTXOR, pending); test_and_ack_op(STRIPE_OP_CHECK, pending); - if (test_and_clear_bit(STRIPE_OP_IO, &sh->ops.pending)) - ack++; sh->ops.count -= ack; if (unlikely(sh->ops.count < 0)) { @@ -399,7 +397,6 @@ static void ops_run_io(struct stripe_head *sh) might_sleep(); - set_bit(STRIPE_IO_STARTED, &sh->state); for (i = disks; i--; ) { int rw; struct bio *bi; @@ -433,6 +430,8 @@ static void ops_run_io(struct stripe_head *sh) test_bit(STRIPE_EXPAND_READY, &sh->state)) md_sync_acct(rdev->bdev, STRIPE_SECTORS); + set_bit(STRIPE_IO_STARTED, &sh->state); + bi->bi_bdev = rdev->bdev; pr_debug("%s: for %llu schedule op %ld on disc %d\n", __func__, (unsigned long long)sh->sector, @@ -900,9 +899,6 @@ static void raid5_run_ops(struct stripe_head *sh, unsigned long pending) if (test_bit(STRIPE_OP_CHECK, &pending)) ops_run_check(sh); - if (test_bit(STRIPE_OP_IO, &pending)) - ops_run_io(sh); - if (overlap_clear) for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; @@ -2013,8 +2009,6 @@ static int __handle_issuing_new_read_requests5(struct stripe_head *sh, */ set_bit(R5_LOCKED, &dev->flags); set_bit(R5_Wantread, &dev->flags); - if (!test_and_set_bit(STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; s->locked++; pr_debug("Reading block %d (sync=%d)\n", disk_idx, s->syncing); @@ -2208,9 +2202,6 @@ static void handle_issuing_new_write_requests5(raid5_conf_t *conf, "%d for r-m-w\n", i); set_bit(R5_LOCKED, &dev->flags); set_bit(R5_Wantread, &dev->flags); - if (!test_and_set_bit( - STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; s->locked++; } else { set_bit(STRIPE_DELAYED, &sh->state); @@ -2234,9 +2225,6 @@ static void handle_issuing_new_write_requests5(raid5_conf_t *conf, "%d for Reconstruct\n", i); set_bit(R5_LOCKED, &dev->flags); set_bit(R5_Wantread, &dev->flags); - if (!test_and_set_bit( - STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; s->locked++; } else { set_bit(STRIPE_DELAYED, &sh->state); @@ -2444,8 +2432,6 @@ static void handle_parity_checks5(raid5_conf_t *conf, struct stripe_head *sh, set_bit(R5_LOCKED, &dev->flags); set_bit(R5_Wantwrite, &dev->flags); - if (!test_and_set_bit(STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; clear_bit(STRIPE_DEGRADED, &sh->state); s->locked++; @@ -2801,9 +2787,6 @@ static void handle_stripe5(struct stripe_head *sh) (i == sh->pd_idx || dev->written)) { pr_debug("Writing block %d\n", i); set_bit(R5_Wantwrite, &dev->flags); - if (!test_and_set_bit( - STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; if (prexor) continue; if (!test_bit(R5_Insync, &dev->flags) || @@ -2857,16 +2840,12 @@ static void handle_stripe5(struct stripe_head *sh) dev = &sh->dev[s.failed_num]; if (!test_bit(R5_ReWrite, &dev->flags)) { set_bit(R5_Wantwrite, &dev->flags); - if (!test_and_set_bit(STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; set_bit(R5_ReWrite, &dev->flags); set_bit(R5_LOCKED, &dev->flags); s.locked++; } else { /* let's read it back */ set_bit(R5_Wantread, &dev->flags); - if (!test_and_set_bit(STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; set_bit(R5_LOCKED, &dev->flags); s.locked++; } @@ -2884,13 +2863,10 @@ static void handle_stripe5(struct stripe_head *sh) clear_bit(STRIPE_OP_POSTXOR, &sh->ops.ack); clear_bit(STRIPE_OP_POSTXOR, &sh->ops.complete); - for (i = conf->raid_disks; i--; ) { + for (i = conf->raid_disks; i--; ) set_bit(R5_Wantwrite, &sh->dev[i].flags); set_bit(R5_LOCKED, &dev->flags); s.locked++; - if (!test_and_set_bit(STRIPE_OP_IO, &sh->ops.pending)) - sh->ops.count++; - } } if (s.expanded && test_bit(STRIPE_EXPANDING, &sh->state) && @@ -2926,6 +2902,8 @@ static void handle_stripe5(struct stripe_head *sh) if (pending) raid5_run_ops(sh, pending); + ops_run_io(sh); + return_io(return_bi); } diff --git a/include/linux/raid/raid5.h b/include/linux/raid/raid5.h index 4ecae31a3dcb..1301195abf4b 100644 --- a/include/linux/raid/raid5.h +++ b/include/linux/raid/raid5.h @@ -263,7 +263,6 @@ struct r6_state { #define STRIPE_OP_BIODRAIN 3 #define STRIPE_OP_POSTXOR 4 #define STRIPE_OP_CHECK 5 -#define STRIPE_OP_IO 6 /* modifiers to the base operations * STRIPE_OP_MOD_REPAIR_PD - compute the parity block and write it back -- cgit v1.2.3 From ecc65c9b3f9b9d740a5deade3d85b39be56401b6 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sat, 28 Jun 2008 08:31:57 +1000 Subject: md: replace STRIPE_OP_CHECK with 'check_states' From: Dan Williams The STRIPE_OP_* flags record the state of stripe operations which are performed outside the stripe lock. Their use in indicating which operations need to be run is straightforward; however, interpolating what the next state of the stripe should be based on a given combination of these flags is not straightforward, and has led to bugs. An easier to read implementation with minimal degrees of freedom is needed. Towards this goal, this patch introduces explicit states to replace what was previously interpolated from the STRIPE_OP_* flags. For now this only converts the handle_parity_checks5 path, removing a user of the ops.{pending,ack,complete,count} fields of struct stripe_operations. This conversion also found a remaining issue with the current code. There is a small window for a drive to fail between when we schedule a repair and when the parity calculation for that repair completes. When this happens we will writeback to 'failed_num' when we really want to write back to 'pd_idx'. Signed-off-by: Dan Williams Signed-off-by: Neil Brown --- drivers/md/raid5.c | 172 ++++++++++++++++++++++----------------------- include/linux/raid/raid5.h | 46 ++++++++++-- 2 files changed, 123 insertions(+), 95 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 6f3dd12dd3a4..544e1600f208 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -605,7 +605,11 @@ static void ops_complete_compute5(void *stripe_head_ref) set_bit(R5_UPTODATE, &tgt->flags); BUG_ON(!test_bit(R5_Wantcompute, &tgt->flags)); clear_bit(R5_Wantcompute, &tgt->flags); - set_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.complete); + clear_bit(STRIPE_COMPUTE_RUN, &sh->state); + if (sh->check_state == check_state_compute_run) + sh->check_state = check_state_compute_result; + else + set_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.complete); set_bit(STRIPE_HANDLE, &sh->state); release_stripe(sh); } @@ -838,7 +842,7 @@ static void ops_complete_check(void *stripe_head_ref) pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); - set_bit(STRIPE_OP_CHECK, &sh->ops.complete); + sh->check_state = check_state_check_result; set_bit(STRIPE_HANDLE, &sh->state); release_stripe(sh); } @@ -870,7 +874,8 @@ static void ops_run_check(struct stripe_head *sh) ops_complete_check, sh); } -static void raid5_run_ops(struct stripe_head *sh, unsigned long pending) +static void raid5_run_ops(struct stripe_head *sh, unsigned long pending, + unsigned long ops_request) { int overlap_clear = 0, i, disks = sh->disks; struct dma_async_tx_descriptor *tx = NULL; @@ -880,7 +885,8 @@ static void raid5_run_ops(struct stripe_head *sh, unsigned long pending) overlap_clear++; } - if (test_bit(STRIPE_OP_COMPUTE_BLK, &pending)) + if (test_bit(STRIPE_OP_COMPUTE_BLK, &pending) || + test_bit(STRIPE_OP_COMPUTE_BLK, &ops_request)) tx = ops_run_compute5(sh, pending); if (test_bit(STRIPE_OP_PREXOR, &pending)) @@ -894,7 +900,7 @@ static void raid5_run_ops(struct stripe_head *sh, unsigned long pending) if (test_bit(STRIPE_OP_POSTXOR, &pending)) ops_run_postxor(sh, tx, pending); - if (test_bit(STRIPE_OP_CHECK, &pending)) + if (test_bit(STRIPE_OP_CHECK, &ops_request)) ops_run_check(sh); if (overlap_clear) @@ -1961,8 +1967,7 @@ static int __handle_issuing_new_read_requests5(struct stripe_head *sh, /* don't schedule compute operations or reads on the parity block while * a check is in flight */ - if ((disk_idx == sh->pd_idx) && - test_bit(STRIPE_OP_CHECK, &sh->ops.pending)) + if (disk_idx == sh->pd_idx && sh->check_state) return ~0; /* is the data in this block needed, and can we get it? */ @@ -1983,9 +1988,8 @@ static int __handle_issuing_new_read_requests5(struct stripe_head *sh, * 3/ We hold off parity block re-reads until check operations * have quiesced. */ - if ((s->uptodate == disks - 1) && - (s->failed && disk_idx == s->failed_num) && - !test_bit(STRIPE_OP_CHECK, &sh->ops.pending)) { + if ((s->uptodate == disks - 1) && !sh->check_state && + (s->failed && disk_idx == s->failed_num)) { set_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.pending); set_bit(R5_Wantcompute, &dev->flags); sh->ops.target = disk_idx; @@ -2021,12 +2025,8 @@ static void handle_issuing_new_read_requests5(struct stripe_head *sh, { int i; - /* Clear completed compute operations. Parity recovery - * (STRIPE_OP_MOD_REPAIR_PD) implies a write-back which is handled - * later on in this routine - */ - if (test_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.complete) && - !test_bit(STRIPE_OP_MOD_REPAIR_PD, &sh->ops.pending)) { + /* Clear completed compute operations */ + if (test_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.complete)) { clear_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.complete); clear_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.ack); clear_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.pending); @@ -2350,90 +2350,85 @@ static void handle_issuing_new_write_requests6(raid5_conf_t *conf, static void handle_parity_checks5(raid5_conf_t *conf, struct stripe_head *sh, struct stripe_head_state *s, int disks) { - int canceled_check = 0; + struct r5dev *dev = NULL; set_bit(STRIPE_HANDLE, &sh->state); - /* complete a check operation */ - if (test_and_clear_bit(STRIPE_OP_CHECK, &sh->ops.complete)) { - clear_bit(STRIPE_OP_CHECK, &sh->ops.ack); - clear_bit(STRIPE_OP_CHECK, &sh->ops.pending); + switch (sh->check_state) { + case check_state_idle: + /* start a new check operation if there are no failures */ if (s->failed == 0) { - if (sh->ops.zero_sum_result == 0) - /* parity is correct (on disc, - * not in buffer any more) - */ - set_bit(STRIPE_INSYNC, &sh->state); - else { - conf->mddev->resync_mismatches += - STRIPE_SECTORS; - if (test_bit( - MD_RECOVERY_CHECK, &conf->mddev->recovery)) - /* don't try to repair!! */ - set_bit(STRIPE_INSYNC, &sh->state); - else { - set_bit(STRIPE_OP_COMPUTE_BLK, - &sh->ops.pending); - set_bit(STRIPE_OP_MOD_REPAIR_PD, - &sh->ops.pending); - set_bit(R5_Wantcompute, - &sh->dev[sh->pd_idx].flags); - sh->ops.target = sh->pd_idx; - sh->ops.count++; - s->uptodate++; - } - } - } else - canceled_check = 1; /* STRIPE_INSYNC is not set */ - } - - /* start a new check operation if there are no failures, the stripe is - * not insync, and a repair is not in flight - */ - if (s->failed == 0 && - !test_bit(STRIPE_INSYNC, &sh->state) && - !test_bit(STRIPE_OP_MOD_REPAIR_PD, &sh->ops.pending)) { - if (!test_and_set_bit(STRIPE_OP_CHECK, &sh->ops.pending)) { BUG_ON(s->uptodate != disks); + sh->check_state = check_state_run; + set_bit(STRIPE_OP_CHECK, &s->ops_request); clear_bit(R5_UPTODATE, &sh->dev[sh->pd_idx].flags); - sh->ops.count++; s->uptodate--; + break; } - } - - /* check if we can clear a parity disk reconstruct */ - if (test_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.complete) && - test_bit(STRIPE_OP_MOD_REPAIR_PD, &sh->ops.pending)) { - - clear_bit(STRIPE_OP_MOD_REPAIR_PD, &sh->ops.pending); - clear_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.complete); - clear_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.ack); - clear_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.pending); - } - + dev = &sh->dev[s->failed_num]; + /* fall through */ + case check_state_compute_result: + sh->check_state = check_state_idle; + if (!dev) + dev = &sh->dev[sh->pd_idx]; + + /* check that a write has not made the stripe insync */ + if (test_bit(STRIPE_INSYNC, &sh->state)) + break; - /* Wait for check parity and compute block operations to complete - * before write-back. If a failure occurred while the check operation - * was in flight we need to cycle this stripe through handle_stripe - * since the parity block may not be uptodate - */ - if (!canceled_check && !test_bit(STRIPE_INSYNC, &sh->state) && - !test_bit(STRIPE_OP_CHECK, &sh->ops.pending) && - !test_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.pending)) { - struct r5dev *dev; /* either failed parity check, or recovery is happening */ - if (s->failed == 0) - s->failed_num = sh->pd_idx; - dev = &sh->dev[s->failed_num]; BUG_ON(!test_bit(R5_UPTODATE, &dev->flags)); BUG_ON(s->uptodate != disks); set_bit(R5_LOCKED, &dev->flags); + s->locked++; set_bit(R5_Wantwrite, &dev->flags); clear_bit(STRIPE_DEGRADED, &sh->state); - s->locked++; set_bit(STRIPE_INSYNC, &sh->state); + break; + case check_state_run: + break; /* we will be called again upon completion */ + case check_state_check_result: + sh->check_state = check_state_idle; + + /* if a failure occurred during the check operation, leave + * STRIPE_INSYNC not set and let the stripe be handled again + */ + if (s->failed) + break; + + /* handle a successful check operation, if parity is correct + * we are done. Otherwise update the mismatch count and repair + * parity if !MD_RECOVERY_CHECK + */ + if (sh->ops.zero_sum_result == 0) + /* parity is correct (on disc, + * not in buffer any more) + */ + set_bit(STRIPE_INSYNC, &sh->state); + else { + conf->mddev->resync_mismatches += STRIPE_SECTORS; + if (test_bit(MD_RECOVERY_CHECK, &conf->mddev->recovery)) + /* don't try to repair!! */ + set_bit(STRIPE_INSYNC, &sh->state); + else { + sh->check_state = check_state_compute_run; + set_bit(STRIPE_OP_COMPUTE_BLK, &s->ops_request); + set_bit(R5_Wantcompute, + &sh->dev[sh->pd_idx].flags); + sh->ops.target = sh->pd_idx; + s->uptodate++; + } + } + break; + case check_state_compute_run: + break; + default: + printk(KERN_ERR "%s: unknown check_state: %d sector: %llu\n", + __func__, sh->check_state, + (unsigned long long) sh->sector); + BUG(); } } @@ -2807,7 +2802,7 @@ static void handle_stripe5(struct stripe_head *sh) * block. */ if (s.to_write && !test_bit(STRIPE_OP_POSTXOR, &sh->ops.pending) && - !test_bit(STRIPE_OP_CHECK, &sh->ops.pending)) + !sh->check_state) handle_issuing_new_write_requests5(conf, sh, &s, disks); /* maybe we need to check and possibly fix the parity for this stripe @@ -2815,11 +2810,10 @@ static void handle_stripe5(struct stripe_head *sh) * data is available. The parity check is held off while parity * dependent operations are in flight. */ - if ((s.syncing && s.locked == 0 && + if (sh->check_state || + (s.syncing && s.locked == 0 && !test_bit(STRIPE_OP_COMPUTE_BLK, &sh->ops.pending) && - !test_bit(STRIPE_INSYNC, &sh->state)) || - test_bit(STRIPE_OP_CHECK, &sh->ops.pending) || - test_bit(STRIPE_OP_MOD_REPAIR_PD, &sh->ops.pending)) + !test_bit(STRIPE_INSYNC, &sh->state))) handle_parity_checks5(conf, sh, &s, disks); if (s.syncing && s.locked == 0 && test_bit(STRIPE_INSYNC, &sh->state)) { @@ -2897,8 +2891,8 @@ static void handle_stripe5(struct stripe_head *sh) if (unlikely(blocked_rdev)) md_wait_for_blocked_rdev(blocked_rdev, conf->mddev); - if (pending) - raid5_run_ops(sh, pending); + if (pending || s.ops_request) + raid5_run_ops(sh, pending, s.ops_request); ops_run_io(sh, &s); diff --git a/include/linux/raid/raid5.h b/include/linux/raid/raid5.h index 1301195abf4b..2c96d5fd54bf 100644 --- a/include/linux/raid/raid5.h +++ b/include/linux/raid/raid5.h @@ -158,6 +158,41 @@ * the compute block completes. */ +/* + * Operations state - intermediate states that are visible outside of sh->lock + * In general _idle indicates nothing is running, _run indicates a data + * processing operation is active, and _result means the data processing result + * is stable and can be acted upon. For simple operations like biofill and + * compute that only have an _idle and _run state they are indicated with + * sh->state flags (STRIPE_BIOFILL_RUN and STRIPE_COMPUTE_RUN) + */ +/** + * enum check_states - handles syncing / repairing a stripe + * @check_state_idle - check operations are quiesced + * @check_state_run - check operation is running + * @check_state_result - set outside lock when check result is valid + * @check_state_compute_run - check failed and we are repairing + * @check_state_compute_result - set outside lock when compute result is valid + */ +enum check_states { + check_state_idle = 0, + check_state_run, /* parity check */ + check_state_check_result, + check_state_compute_run, /* parity repair */ + check_state_compute_result, +}; + +/** + * enum reconstruct_states - handles writing or expanding a stripe + */ +enum reconstruct_states { + reconstruct_state_idle = 0, + reconstruct_state_drain_run, /* write */ + reconstruct_state_run, /* expand */ + reconstruct_state_drain_result, + reconstruct_state_result, +}; + struct stripe_head { struct hlist_node hash; struct list_head lru; /* inactive_list or handle_list */ @@ -169,6 +204,7 @@ struct stripe_head { spinlock_t lock; int bm_seq; /* sequence number for bitmap flushes */ int disks; /* disks in stripe */ + enum check_states check_state; /* stripe_operations * @pending - pending ops flags (set for request->issue->complete) * @ack - submitted ops flags (set for issue->complete) @@ -202,6 +238,7 @@ struct stripe_head_state { int locked, uptodate, to_read, to_write, failed, written; int to_fill, compute, req_compute, non_overwrite; int failed_num; + unsigned long ops_request; }; /* r6_state - extra state data only relevant to r6 */ @@ -254,8 +291,10 @@ struct r6_state { #define STRIPE_EXPAND_READY 11 #define STRIPE_IO_STARTED 12 /* do not count towards 'bypass_count' */ #define STRIPE_FULL_WRITE 13 /* all blocks are set to be overwritten */ +#define STRIPE_BIOFILL_RUN 14 +#define STRIPE_COMPUTE_RUN 15 /* - * Operations flags (in issue order) + * Operation request flags */ #define STRIPE_OP_BIOFILL 0 #define STRIPE_OP_COMPUTE_BLK 1 @@ -264,11 +303,6 @@ struct r6_state { #define STRIPE_OP_POSTXOR 4 #define STRIPE_OP_CHECK 5 -/* modifiers to the base operations - * STRIPE_OP_MOD_REPAIR_PD - compute the parity block and write it back - */ -#define STRIPE_OP_MOD_REPAIR_PD 7 - /* * Plugging: * -- cgit v1.2.3 From 600aa10993012ff2dd5617720dac081e4f992017 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sat, 28 Jun 2008 08:32:05 +1000 Subject: md: replace STRIPE_OP_{BIODRAIN,PREXOR,POSTXOR} with 'reconstruct_states' From: Dan Williams Track the state of reconstruct operations (recalculating the parity block usually due to incoming writes, or as part of array expansion) Reduces the scope of the STRIPE_OP_{BIODRAIN,PREXOR,POSTXOR} flags to only tracking whether a reconstruct operation has been requested via the ops_request field of struct stripe_head_state. This is the final step in the removal of ops.{pending,ack,complete,count}, i.e. the STRIPE_OP_{BIODRAIN,PREXOR,POSTXOR} flags only request an operation and do not track the state of the operation. Signed-off-by: Dan Williams Signed-off-by: Neil Brown --- drivers/md/raid5.c | 204 ++++++++++++++------------------------------- include/linux/raid/raid5.h | 9 +- 2 files changed, 63 insertions(+), 150 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 835046bf384e..b9159367491a 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -122,6 +122,13 @@ static void return_io(struct bio *return_bi) static void print_raid5_conf (raid5_conf_t *conf); +static int stripe_operations_active(struct stripe_head *sh) +{ + return sh->check_state || sh->reconstruct_state || + test_bit(STRIPE_BIOFILL_RUN, &sh->state) || + test_bit(STRIPE_COMPUTE_RUN, &sh->state); +} + static void __release_stripe(raid5_conf_t *conf, struct stripe_head *sh) { if (atomic_dec_and_test(&sh->count)) { @@ -141,7 +148,7 @@ static void __release_stripe(raid5_conf_t *conf, struct stripe_head *sh) } md_wakeup_thread(conf->mddev->thread); } else { - BUG_ON(sh->ops.pending); + BUG_ON(stripe_operations_active(sh)); if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) { atomic_dec(&conf->preread_active_stripes); if (atomic_read(&conf->preread_active_stripes) < IO_THRESHOLD) @@ -243,7 +250,7 @@ static void init_stripe(struct stripe_head *sh, sector_t sector, int pd_idx, int BUG_ON(atomic_read(&sh->count) != 0); BUG_ON(test_bit(STRIPE_HANDLE, &sh->state)); - BUG_ON(sh->ops.pending || sh->ops.ack || sh->ops.complete); + BUG_ON(stripe_operations_active(sh)); CHECK_DEVLOCK(); pr_debug("init_stripe called, stripe %llu\n", @@ -344,47 +351,6 @@ static struct stripe_head *get_active_stripe(raid5_conf_t *conf, sector_t sector return sh; } -/* test_and_ack_op() ensures that we only dequeue an operation once */ -#define test_and_ack_op(op, pend) \ -do { \ - if (test_bit(op, &sh->ops.pending) && \ - !test_bit(op, &sh->ops.complete)) { \ - if (test_and_set_bit(op, &sh->ops.ack)) \ - clear_bit(op, &pend); \ - else \ - ack++; \ - } else \ - clear_bit(op, &pend); \ -} while (0) - -/* find new work to run, do not resubmit work that is already - * in flight - */ -static unsigned long get_stripe_work(struct stripe_head *sh) -{ - unsigned long pending; - int ack = 0; - - pending = sh->ops.pending; - - test_and_ack_op(STRIPE_OP_BIOFILL, pending); - test_and_ack_op(STRIPE_OP_COMPUTE_BLK, pending); - test_and_ack_op(STRIPE_OP_PREXOR, pending); - test_and_ack_op(STRIPE_OP_BIODRAIN, pending); - test_and_ack_op(STRIPE_OP_POSTXOR, pending); - test_and_ack_op(STRIPE_OP_CHECK, pending); - - sh->ops.count -= ack; - if (unlikely(sh->ops.count < 0)) { - printk(KERN_ERR "pending: %#lx ops.pending: %#lx ops.ack: %#lx " - "ops.complete: %#lx\n", pending, sh->ops.pending, - sh->ops.ack, sh->ops.complete); - BUG(); - } - - return pending; -} - static void raid5_end_read_request(struct bio *bi, int error); static void @@ -609,7 +575,7 @@ static void ops_complete_compute5(void *stripe_head_ref) } static struct dma_async_tx_descriptor * -ops_run_compute5(struct stripe_head *sh, unsigned long pending) +ops_run_compute5(struct stripe_head *sh, unsigned long ops_request) { /* kernel stack size limits the total number of disks */ int disks = sh->disks; @@ -640,7 +606,7 @@ ops_run_compute5(struct stripe_head *sh, unsigned long pending) ops_complete_compute5, sh); /* ack now if postxor is not set to be run */ - if (tx && !test_bit(STRIPE_OP_POSTXOR, &pending)) + if (tx && !test_bit(STRIPE_OP_POSTXOR, &ops_request)) async_tx_ack(tx); return tx; @@ -652,8 +618,6 @@ static void ops_complete_prexor(void *stripe_head_ref) pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); - - set_bit(STRIPE_OP_PREXOR, &sh->ops.complete); } static struct dma_async_tx_descriptor * @@ -686,7 +650,7 @@ ops_run_prexor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx) static struct dma_async_tx_descriptor * ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, - unsigned long pending) + unsigned long ops_request) { int disks = sh->disks; int pd_idx = sh->pd_idx, i; @@ -694,7 +658,7 @@ ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, /* check if prexor is active which means only process blocks * that are part of a read-modify-write (Wantprexor) */ - int prexor = test_bit(STRIPE_OP_PREXOR, &pending); + int prexor = test_bit(STRIPE_OP_PREXOR, &ops_request); pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); @@ -744,7 +708,7 @@ static void ops_complete_postxor(void *stripe_head_ref) pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); - set_bit(STRIPE_OP_POSTXOR, &sh->ops.complete); + sh->reconstruct_state = reconstruct_state_result; set_bit(STRIPE_HANDLE, &sh->state); release_stripe(sh); } @@ -763,16 +727,14 @@ static void ops_complete_write(void *stripe_head_ref) set_bit(R5_UPTODATE, &dev->flags); } - set_bit(STRIPE_OP_BIODRAIN, &sh->ops.complete); - set_bit(STRIPE_OP_POSTXOR, &sh->ops.complete); - + sh->reconstruct_state = reconstruct_state_drain_result; set_bit(STRIPE_HANDLE, &sh->state); release_stripe(sh); } static void ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, - unsigned long pending) + unsigned long ops_request) { /* kernel stack size limits the total number of disks */ int disks = sh->disks; @@ -780,7 +742,7 @@ ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, int count = 0, pd_idx = sh->pd_idx, i; struct page *xor_dest; - int prexor = test_bit(STRIPE_OP_PREXOR, &pending); + int prexor = test_bit(STRIPE_OP_PREXOR, &ops_request); unsigned long flags; dma_async_tx_callback callback; @@ -807,7 +769,7 @@ ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, } /* check whether this postxor is part of a write */ - callback = test_bit(STRIPE_OP_BIODRAIN, &pending) ? + callback = test_bit(STRIPE_OP_BIODRAIN, &ops_request) ? ops_complete_write : ops_complete_postxor; /* 1/ if we prexor'd then the dest is reused as a source @@ -868,8 +830,7 @@ static void ops_run_check(struct stripe_head *sh) ops_complete_check, sh); } -static void raid5_run_ops(struct stripe_head *sh, unsigned long pending, - unsigned long ops_request) +static void raid5_run_ops(struct stripe_head *sh, unsigned long ops_request) { int overlap_clear = 0, i, disks = sh->disks; struct dma_async_tx_descriptor *tx = NULL; @@ -880,18 +841,18 @@ static void raid5_run_ops(struct stripe_head *sh, unsigned long pending, } if (test_bit(STRIPE_OP_COMPUTE_BLK, &ops_request)) - tx = ops_run_compute5(sh, pending); + tx = ops_run_compute5(sh, ops_request); - if (test_bit(STRIPE_OP_PREXOR, &pending)) + if (test_bit(STRIPE_OP_PREXOR, &ops_request)) tx = ops_run_prexor(sh, tx); - if (test_bit(STRIPE_OP_BIODRAIN, &pending)) { - tx = ops_run_biodrain(sh, tx, pending); + if (test_bit(STRIPE_OP_BIODRAIN, &ops_request)) { + tx = ops_run_biodrain(sh, tx, ops_request); overlap_clear++; } - if (test_bit(STRIPE_OP_POSTXOR, &pending)) - ops_run_postxor(sh, tx, pending); + if (test_bit(STRIPE_OP_POSTXOR, &ops_request)) + ops_run_postxor(sh, tx, ops_request); if (test_bit(STRIPE_OP_CHECK, &ops_request)) ops_run_check(sh); @@ -1684,11 +1645,11 @@ static void compute_block_2(struct stripe_head *sh, int dd_idx1, int dd_idx2) } } -static int -handle_write_operations5(struct stripe_head *sh, int rcw, int expand) +static void +handle_write_operations5(struct stripe_head *sh, struct stripe_head_state *s, + int rcw, int expand) { int i, pd_idx = sh->pd_idx, disks = sh->disks; - int locked = 0; if (rcw) { /* if we are not expanding this is a proper write request, and @@ -1696,12 +1657,12 @@ handle_write_operations5(struct stripe_head *sh, int rcw, int expand) * stripe cache */ if (!expand) { - set_bit(STRIPE_OP_BIODRAIN, &sh->ops.pending); - sh->ops.count++; - } + sh->reconstruct_state = reconstruct_state_drain_run; + set_bit(STRIPE_OP_BIODRAIN, &s->ops_request); + } else + sh->reconstruct_state = reconstruct_state_run; - set_bit(STRIPE_OP_POSTXOR, &sh->ops.pending); - sh->ops.count++; + set_bit(STRIPE_OP_POSTXOR, &s->ops_request); for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; @@ -1710,21 +1671,20 @@ handle_write_operations5(struct stripe_head *sh, int rcw, int expand) set_bit(R5_LOCKED, &dev->flags); if (!expand) clear_bit(R5_UPTODATE, &dev->flags); - locked++; + s->locked++; } } - if (locked + 1 == disks) + if (s->locked + 1 == disks) if (!test_and_set_bit(STRIPE_FULL_WRITE, &sh->state)) atomic_inc(&sh->raid_conf->pending_full_writes); } else { BUG_ON(!(test_bit(R5_UPTODATE, &sh->dev[pd_idx].flags) || test_bit(R5_Wantcompute, &sh->dev[pd_idx].flags))); - set_bit(STRIPE_OP_PREXOR, &sh->ops.pending); - set_bit(STRIPE_OP_BIODRAIN, &sh->ops.pending); - set_bit(STRIPE_OP_POSTXOR, &sh->ops.pending); - - sh->ops.count += 3; + sh->reconstruct_state = reconstruct_state_drain_run; + set_bit(STRIPE_OP_PREXOR, &s->ops_request); + set_bit(STRIPE_OP_BIODRAIN, &s->ops_request); + set_bit(STRIPE_OP_POSTXOR, &s->ops_request); for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; @@ -1742,7 +1702,7 @@ handle_write_operations5(struct stripe_head *sh, int rcw, int expand) set_bit(R5_Wantprexor, &dev->flags); set_bit(R5_LOCKED, &dev->flags); clear_bit(R5_UPTODATE, &dev->flags); - locked++; + s->locked++; } } } @@ -1752,13 +1712,11 @@ handle_write_operations5(struct stripe_head *sh, int rcw, int expand) */ set_bit(R5_LOCKED, &sh->dev[pd_idx].flags); clear_bit(R5_UPTODATE, &sh->dev[pd_idx].flags); - locked++; + s->locked++; - pr_debug("%s: stripe %llu locked: %d pending: %lx\n", + pr_debug("%s: stripe %llu locked: %d ops_request: %lx\n", __func__, (unsigned long long)sh->sector, - locked, sh->ops.pending); - - return locked; + s->locked, s->ops_request); } /* @@ -2005,8 +1963,7 @@ static void handle_issuing_new_read_requests5(struct stripe_head *sh, * midst of changing due to a write */ if (!test_bit(STRIPE_COMPUTE_RUN, &sh->state) && !sh->check_state && - !test_bit(STRIPE_OP_PREXOR, &sh->ops.pending) && - !test_bit(STRIPE_OP_POSTXOR, &sh->ops.pending)) { + !sh->reconstruct_state) { for (i = disks; i--; ) if (__handle_issuing_new_read_requests5( sh, s, i, disks) == 0) @@ -2211,7 +2168,7 @@ static void handle_issuing_new_write_requests5(raid5_conf_t *conf, if ((s->req_compute || !test_bit(STRIPE_COMPUTE_RUN, &sh->state)) && (s->locked == 0 && (rcw == 0 || rmw == 0) && !test_bit(STRIPE_BIT_DELAY, &sh->state))) - s->locked += handle_write_operations5(sh, rcw == 0, 0); + handle_write_operations5(sh, s, rcw == 0, 0); } static void handle_issuing_new_write_requests6(raid5_conf_t *conf, @@ -2581,15 +2538,14 @@ static void handle_stripe5(struct stripe_head *sh) struct bio *return_bi = NULL; struct stripe_head_state s; struct r5dev *dev; - unsigned long pending = 0; mdk_rdev_t *blocked_rdev = NULL; int prexor; memset(&s, 0, sizeof(s)); - pr_debug("handling stripe %llu, state=%#lx cnt=%d, pd_idx=%d " - "ops=%lx:%lx:%lx\n", (unsigned long long)sh->sector, sh->state, - atomic_read(&sh->count), sh->pd_idx, - sh->ops.pending, sh->ops.ack, sh->ops.complete); + pr_debug("handling stripe %llu, state=%#lx cnt=%d, pd_idx=%d check:%d " + "reconstruct:%d\n", (unsigned long long)sh->sector, sh->state, + atomic_read(&sh->count), sh->pd_idx, sh->check_state, + sh->reconstruct_state); spin_lock(&sh->lock); clear_bit(STRIPE_HANDLE, &sh->state); @@ -2703,34 +2659,12 @@ static void handle_stripe5(struct stripe_head *sh) /* Now we check to see if any write operations have recently * completed */ - - /* leave prexor set until postxor is done, allows us to distinguish - * a rmw from a rcw during biodrain - */ prexor = 0; - if (test_bit(STRIPE_OP_PREXOR, &sh->ops.complete) && - test_bit(STRIPE_OP_POSTXOR, &sh->ops.complete)) { - - prexor = 1; - clear_bit(STRIPE_OP_PREXOR, &sh->ops.complete); - clear_bit(STRIPE_OP_PREXOR, &sh->ops.ack); - clear_bit(STRIPE_OP_PREXOR, &sh->ops.pending); - + if (sh->reconstruct_state == reconstruct_state_drain_result) { + sh->reconstruct_state = reconstruct_state_idle; for (i = disks; i--; ) - clear_bit(R5_Wantprexor, &sh->dev[i].flags); - } - - /* if only POSTXOR is set then this is an 'expand' postxor */ - if (test_bit(STRIPE_OP_BIODRAIN, &sh->ops.complete) && - test_bit(STRIPE_OP_POSTXOR, &sh->ops.complete)) { - - clear_bit(STRIPE_OP_BIODRAIN, &sh->ops.complete); - clear_bit(STRIPE_OP_BIODRAIN, &sh->ops.ack); - clear_bit(STRIPE_OP_BIODRAIN, &sh->ops.pending); - - clear_bit(STRIPE_OP_POSTXOR, &sh->ops.complete); - clear_bit(STRIPE_OP_POSTXOR, &sh->ops.ack); - clear_bit(STRIPE_OP_POSTXOR, &sh->ops.pending); + prexor += test_and_clear_bit(R5_Wantprexor, + &sh->dev[i].flags); /* All the 'written' buffers and the parity block are ready to * be written back to disk @@ -2763,8 +2697,7 @@ static void handle_stripe5(struct stripe_head *sh) * 2/ A 'check' operation is in flight, as it may clobber the parity * block. */ - if (s.to_write && !test_bit(STRIPE_OP_POSTXOR, &sh->ops.pending) && - !sh->check_state) + if (s.to_write && !sh->reconstruct_state && !sh->check_state) handle_issuing_new_write_requests5(conf, sh, &s, disks); /* maybe we need to check and possibly fix the parity for this stripe @@ -2805,18 +2738,10 @@ static void handle_stripe5(struct stripe_head *sh) } } - /* Finish postxor operations initiated by the expansion - * process - */ - if (test_bit(STRIPE_OP_POSTXOR, &sh->ops.complete) && - !test_bit(STRIPE_OP_BIODRAIN, &sh->ops.pending)) { - + /* Finish reconstruct operations initiated by the expansion process */ + if (sh->reconstruct_state == reconstruct_state_result) { + sh->reconstruct_state = reconstruct_state_idle; clear_bit(STRIPE_EXPANDING, &sh->state); - - clear_bit(STRIPE_OP_POSTXOR, &sh->ops.pending); - clear_bit(STRIPE_OP_POSTXOR, &sh->ops.ack); - clear_bit(STRIPE_OP_POSTXOR, &sh->ops.complete); - for (i = conf->raid_disks; i--; ) set_bit(R5_Wantwrite, &sh->dev[i].flags); set_bit(R5_LOCKED, &dev->flags); @@ -2824,15 +2749,13 @@ static void handle_stripe5(struct stripe_head *sh) } if (s.expanded && test_bit(STRIPE_EXPANDING, &sh->state) && - !test_bit(STRIPE_OP_POSTXOR, &sh->ops.pending)) { + !sh->reconstruct_state) { /* Need to write out all blocks after computing parity */ sh->disks = conf->raid_disks; sh->pd_idx = stripe_to_pdidx(sh->sector, conf, conf->raid_disks); - s.locked += handle_write_operations5(sh, 1, 1); - } else if (s.expanded && - s.locked == 0 && - !test_bit(STRIPE_OP_POSTXOR, &sh->ops.pending)) { + handle_write_operations5(sh, &s, 1, 1); + } else if (s.expanded && !sh->reconstruct_state && s.locked == 0) { clear_bit(STRIPE_EXPAND_READY, &sh->state); atomic_dec(&conf->reshape_stripes); wake_up(&conf->wait_for_overlap); @@ -2843,9 +2766,6 @@ static void handle_stripe5(struct stripe_head *sh) !test_bit(STRIPE_COMPUTE_RUN, &sh->state)) handle_stripe_expansion(conf, sh, NULL); - if (sh->ops.count) - pending = get_stripe_work(sh); - unlock: spin_unlock(&sh->lock); @@ -2853,8 +2773,8 @@ static void handle_stripe5(struct stripe_head *sh) if (unlikely(blocked_rdev)) md_wait_for_blocked_rdev(blocked_rdev, conf->mddev); - if (pending || s.ops_request) - raid5_run_ops(sh, pending, s.ops_request); + if (s.ops_request) + raid5_run_ops(sh, s.ops_request); ops_run_io(sh, &s); diff --git a/include/linux/raid/raid5.h b/include/linux/raid/raid5.h index 2c96d5fd54bf..5f3e674b87dd 100644 --- a/include/linux/raid/raid5.h +++ b/include/linux/raid/raid5.h @@ -205,19 +205,12 @@ struct stripe_head { int bm_seq; /* sequence number for bitmap flushes */ int disks; /* disks in stripe */ enum check_states check_state; + enum reconstruct_states reconstruct_state; /* stripe_operations - * @pending - pending ops flags (set for request->issue->complete) - * @ack - submitted ops flags (set for issue->complete) - * @complete - completed ops flags (set for complete) * @target - STRIPE_OP_COMPUTE_BLK target - * @count - raid5_runs_ops is set to run when this is non-zero */ struct stripe_operations { - unsigned long pending; - unsigned long ack; - unsigned long complete; int target; - int count; u32 zero_sum_result; } ops; struct r5dev { -- cgit v1.2.3 From d8ee0728b5b30d7a6f62c399a95e953616d31f23 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sat, 28 Jun 2008 08:32:06 +1000 Subject: md: replace R5_WantPrexor with R5_WantDrain, add 'prexor' reconstruct_states From: Dan Williams Currently ops_run_biodrain and other locations have extra logic to determine which blocks are processed in the prexor and non-prexor cases. This can be eliminated if handle_write_operations5 flags the blocks to be processed in all cases via R5_Wantdrain. The presence of the prexor operation is tracked in sh->reconstruct_state. Signed-off-by: Dan Williams Signed-off-by: Neil Brown --- drivers/md/raid5.c | 89 +++++++++++++++------------------------------- include/linux/raid/raid5.h | 6 ++-- 2 files changed, 32 insertions(+), 63 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index b9159367491a..c71246061c0e 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -637,7 +637,7 @@ ops_run_prexor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx) for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; /* Only process blocks that are known to be uptodate */ - if (dev->towrite && test_bit(R5_Wantprexor, &dev->flags)) + if (test_bit(R5_Wantdrain, &dev->flags)) xor_srcs[count++] = dev->page; } @@ -649,16 +649,10 @@ ops_run_prexor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx) } static struct dma_async_tx_descriptor * -ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, - unsigned long ops_request) +ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx) { int disks = sh->disks; - int pd_idx = sh->pd_idx, i; - - /* check if prexor is active which means only process blocks - * that are part of a read-modify-write (Wantprexor) - */ - int prexor = test_bit(STRIPE_OP_PREXOR, &ops_request); + int i; pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); @@ -666,20 +660,8 @@ ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; struct bio *chosen; - int towrite; - towrite = 0; - if (prexor) { /* rmw */ - if (dev->towrite && - test_bit(R5_Wantprexor, &dev->flags)) - towrite = 1; - } else { /* rcw */ - if (i != pd_idx && dev->towrite && - test_bit(R5_LOCKED, &dev->flags)) - towrite = 1; - } - - if (towrite) { + if (test_and_clear_bit(R5_Wantdrain, &dev->flags)) { struct bio *wbi; spin_lock(&sh->lock); @@ -702,18 +684,6 @@ ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, } static void ops_complete_postxor(void *stripe_head_ref) -{ - struct stripe_head *sh = stripe_head_ref; - - pr_debug("%s: stripe %llu\n", __func__, - (unsigned long long)sh->sector); - - sh->reconstruct_state = reconstruct_state_result; - set_bit(STRIPE_HANDLE, &sh->state); - release_stripe(sh); -} - -static void ops_complete_write(void *stripe_head_ref) { struct stripe_head *sh = stripe_head_ref; int disks = sh->disks, i, pd_idx = sh->pd_idx; @@ -727,14 +697,21 @@ static void ops_complete_write(void *stripe_head_ref) set_bit(R5_UPTODATE, &dev->flags); } - sh->reconstruct_state = reconstruct_state_drain_result; + if (sh->reconstruct_state == reconstruct_state_drain_run) + sh->reconstruct_state = reconstruct_state_drain_result; + else if (sh->reconstruct_state == reconstruct_state_prexor_drain_run) + sh->reconstruct_state = reconstruct_state_prexor_drain_result; + else { + BUG_ON(sh->reconstruct_state != reconstruct_state_run); + sh->reconstruct_state = reconstruct_state_result; + } + set_bit(STRIPE_HANDLE, &sh->state); release_stripe(sh); } static void -ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, - unsigned long ops_request) +ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx) { /* kernel stack size limits the total number of disks */ int disks = sh->disks; @@ -742,9 +719,8 @@ ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, int count = 0, pd_idx = sh->pd_idx, i; struct page *xor_dest; - int prexor = test_bit(STRIPE_OP_PREXOR, &ops_request); + int prexor = 0; unsigned long flags; - dma_async_tx_callback callback; pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); @@ -752,7 +728,8 @@ ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, /* check if prexor is active which means only process blocks * that are part of a read-modify-write (written) */ - if (prexor) { + if (sh->reconstruct_state == reconstruct_state_prexor_drain_run) { + prexor = 1; xor_dest = xor_srcs[count++] = sh->dev[pd_idx].page; for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; @@ -768,10 +745,6 @@ ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, } } - /* check whether this postxor is part of a write */ - callback = test_bit(STRIPE_OP_BIODRAIN, &ops_request) ? - ops_complete_write : ops_complete_postxor; - /* 1/ if we prexor'd then the dest is reused as a source * 2/ if we did not prexor then we are redoing the parity * set ASYNC_TX_XOR_DROP_DST and ASYNC_TX_XOR_ZERO_DST @@ -785,10 +758,10 @@ ops_run_postxor(struct stripe_head *sh, struct dma_async_tx_descriptor *tx, if (unlikely(count == 1)) { flags &= ~(ASYNC_TX_XOR_DROP_DST | ASYNC_TX_XOR_ZERO_DST); tx = async_memcpy(xor_dest, xor_srcs[0], 0, 0, STRIPE_SIZE, - flags, tx, callback, sh); + flags, tx, ops_complete_postxor, sh); } else tx = async_xor(xor_dest, xor_srcs, 0, count, STRIPE_SIZE, - flags, tx, callback, sh); + flags, tx, ops_complete_postxor, sh); } static void ops_complete_check(void *stripe_head_ref) @@ -847,12 +820,12 @@ static void raid5_run_ops(struct stripe_head *sh, unsigned long ops_request) tx = ops_run_prexor(sh, tx); if (test_bit(STRIPE_OP_BIODRAIN, &ops_request)) { - tx = ops_run_biodrain(sh, tx, ops_request); + tx = ops_run_biodrain(sh, tx); overlap_clear++; } if (test_bit(STRIPE_OP_POSTXOR, &ops_request)) - ops_run_postxor(sh, tx, ops_request); + ops_run_postxor(sh, tx); if (test_bit(STRIPE_OP_CHECK, &ops_request)) ops_run_check(sh); @@ -1669,6 +1642,7 @@ handle_write_operations5(struct stripe_head *sh, struct stripe_head_state *s, if (dev->towrite) { set_bit(R5_LOCKED, &dev->flags); + set_bit(R5_Wantdrain, &dev->flags); if (!expand) clear_bit(R5_UPTODATE, &dev->flags); s->locked++; @@ -1681,7 +1655,7 @@ handle_write_operations5(struct stripe_head *sh, struct stripe_head_state *s, BUG_ON(!(test_bit(R5_UPTODATE, &sh->dev[pd_idx].flags) || test_bit(R5_Wantcompute, &sh->dev[pd_idx].flags))); - sh->reconstruct_state = reconstruct_state_drain_run; + sh->reconstruct_state = reconstruct_state_prexor_drain_run; set_bit(STRIPE_OP_PREXOR, &s->ops_request); set_bit(STRIPE_OP_BIODRAIN, &s->ops_request); set_bit(STRIPE_OP_POSTXOR, &s->ops_request); @@ -1691,15 +1665,10 @@ handle_write_operations5(struct stripe_head *sh, struct stripe_head_state *s, if (i == pd_idx) continue; - /* For a read-modify write there may be blocks that are - * locked for reading while others are ready to be - * written so we distinguish these blocks by the - * R5_Wantprexor bit - */ if (dev->towrite && (test_bit(R5_UPTODATE, &dev->flags) || - test_bit(R5_Wantcompute, &dev->flags))) { - set_bit(R5_Wantprexor, &dev->flags); + test_bit(R5_Wantcompute, &dev->flags))) { + set_bit(R5_Wantdrain, &dev->flags); set_bit(R5_LOCKED, &dev->flags); clear_bit(R5_UPTODATE, &dev->flags); s->locked++; @@ -2660,11 +2629,11 @@ static void handle_stripe5(struct stripe_head *sh) * completed */ prexor = 0; - if (sh->reconstruct_state == reconstruct_state_drain_result) { + if (sh->reconstruct_state == reconstruct_state_prexor_drain_result) + prexor = 1; + if (sh->reconstruct_state == reconstruct_state_drain_result || + sh->reconstruct_state == reconstruct_state_prexor_drain_result) { sh->reconstruct_state = reconstruct_state_idle; - for (i = disks; i--; ) - prexor += test_and_clear_bit(R5_Wantprexor, - &sh->dev[i].flags); /* All the 'written' buffers and the parity block are ready to * be written back to disk diff --git a/include/linux/raid/raid5.h b/include/linux/raid/raid5.h index 5f3e674b87dd..3b2672792457 100644 --- a/include/linux/raid/raid5.h +++ b/include/linux/raid/raid5.h @@ -187,8 +187,10 @@ enum check_states { */ enum reconstruct_states { reconstruct_state_idle = 0, + reconstruct_state_prexor_drain_run, /* prexor-write */ reconstruct_state_drain_run, /* write */ reconstruct_state_run, /* expand */ + reconstruct_state_prexor_drain_result, reconstruct_state_drain_result, reconstruct_state_result, }; @@ -258,9 +260,7 @@ struct r6_state { #define R5_Wantfill 12 /* dev->toread contains a bio that needs * filling */ -#define R5_Wantprexor 13 /* distinguish blocks ready for rmw from - * other "towrites" - */ +#define R5_Wantdrain 13 /* dev->towrite needs to be drained */ /* * Write method */ -- cgit v1.2.3 From 818727badc14ce57dc099a075b05505d50b7956e Mon Sep 17 00:00:00 2001 From: Jussi Kivilinna Date: Wed, 18 Jun 2008 15:40:12 +0300 Subject: rndis_host: pass buffer length to rndis_command Pass buffer length to rndis_command so that rndis_command can read full response buffer from device instead of max CONTROL_BUFFER_SIZE bytes. Signed-off-by: Jussi Kivilinna Signed-off-by: Jeff Garzik --- drivers/net/usb/rndis_host.c | 14 +++++++------- drivers/net/wireless/rndis_wlan.c | 4 ++-- include/linux/usb/rndis_host.h | 3 ++- 3 files changed, 11 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c index ae467f182c40..61c98beb4d17 100644 --- a/drivers/net/usb/rndis_host.c +++ b/drivers/net/usb/rndis_host.c @@ -74,7 +74,7 @@ EXPORT_SYMBOL_GPL(rndis_status); * Call context is likely probe(), before interface name is known, * which is why we won't try to use it in the diagnostics. */ -int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf) +int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf, int buflen) { struct cdc_state *info = (void *) &dev->data; int master_ifnum; @@ -121,7 +121,7 @@ int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf) USB_CDC_GET_ENCAPSULATED_RESPONSE, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, master_ifnum, - buf, CONTROL_BUFFER_SIZE, + buf, buflen, RNDIS_CONTROL_TIMEOUT_MS); if (likely(retval >= 8)) { msg_len = le32_to_cpu(buf->msg_len); @@ -239,7 +239,7 @@ static int rndis_query(struct usbnet *dev, struct usb_interface *intf, u.get->len = cpu_to_le32(in_len); u.get->offset = ccpu2(20); - retval = rndis_command(dev, u.header); + retval = rndis_command(dev, u.header, CONTROL_BUFFER_SIZE); if (unlikely(retval < 0)) { dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) failed, %d\n", oid, retval); @@ -328,7 +328,7 @@ generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf, int flags) u.init->max_transfer_size = cpu_to_le32(dev->rx_urb_size); net->change_mtu = NULL; - retval = rndis_command(dev, u.header); + retval = rndis_command(dev, u.header, CONTROL_BUFFER_SIZE); if (unlikely(retval < 0)) { /* it might not even be an RNDIS device!! */ dev_err(&intf->dev, "RNDIS init failed, %d\n", retval); @@ -409,7 +409,7 @@ generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf, int flags) u.set->offset = ccpu2((sizeof *u.set) - 8); *(__le32 *)(u.buf + sizeof *u.set) = RNDIS_DEFAULT_FILTER; - retval = rndis_command(dev, u.header); + retval = rndis_command(dev, u.header, CONTROL_BUFFER_SIZE); if (unlikely(retval < 0)) { dev_err(&intf->dev, "rndis set packet filter, %d\n", retval); goto halt_fail_and_release; @@ -424,7 +424,7 @@ halt_fail_and_release: memset(u.halt, 0, sizeof *u.halt); u.halt->msg_type = RNDIS_MSG_HALT; u.halt->msg_len = ccpu2(sizeof *u.halt); - (void) rndis_command(dev, (void *)u.halt); + (void) rndis_command(dev, (void *)u.halt, CONTROL_BUFFER_SIZE); fail_and_release: usb_set_intfdata(info->data, NULL); usb_driver_release_interface(driver_of(intf), info->data); @@ -449,7 +449,7 @@ void rndis_unbind(struct usbnet *dev, struct usb_interface *intf) if (halt) { halt->msg_type = RNDIS_MSG_HALT; halt->msg_len = ccpu2(sizeof *halt); - (void) rndis_command(dev, (void *)halt); + (void) rndis_command(dev, (void *)halt, CONTROL_BUFFER_SIZE); kfree(halt); } diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index a36d2c85e26e..f001f2afd05e 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -448,7 +448,7 @@ static int rndis_query_oid(struct usbnet *dev, __le32 oid, void *data, int *len) u.get->msg_len = ccpu2(sizeof *u.get); u.get->oid = oid; - ret = rndis_command(dev, u.header); + ret = rndis_command(dev, u.header, buflen); if (ret == 0) { ret = le32_to_cpu(u.get_c->len); *len = (*len > ret) ? ret : *len; @@ -498,7 +498,7 @@ static int rndis_set_oid(struct usbnet *dev, __le32 oid, void *data, int len) u.set->handle = ccpu2(0); memcpy(u.buf + sizeof(*u.set), data, len); - ret = rndis_command(dev, u.header); + ret = rndis_command(dev, u.header, buflen); if (ret == 0) ret = rndis_error_status(u.set_c->status); diff --git a/include/linux/usb/rndis_host.h b/include/linux/usb/rndis_host.h index 29d6458ecb8d..0a6e6d4b929a 100644 --- a/include/linux/usb/rndis_host.h +++ b/include/linux/usb/rndis_host.h @@ -260,7 +260,8 @@ struct rndis_keepalive_c { /* IN (optionally OUT) */ extern void rndis_status(struct usbnet *dev, struct urb *urb); -extern int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf); +extern int +rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf, int buflen); extern int generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf, int flags); extern void rndis_unbind(struct usbnet *dev, struct usb_interface *intf); -- cgit v1.2.3 From 3cadd2d98972f806165c634553ac4918b2b7920c Mon Sep 17 00:00:00 2001 From: Richard Lemon Date: Thu, 26 Jun 2008 10:10:41 -0400 Subject: Input: Add driver for iNexio serial touchscreen. Signed-off-by: Richard Lemon Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 12 +++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/inexio.c | 207 +++++++++++++++++++++++++++++++++++++ include/linux/serio.h | 1 + 4 files changed, 221 insertions(+) create mode 100644 drivers/input/touchscreen/inexio.c (limited to 'include/linux') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 030a89734855..4085791e7be2 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -103,6 +103,18 @@ config TOUCHSCREEN_MTOUCH To compile this driver as a module, choose M here: the module will be called mtouch. +config TOUCHSCREEN_INEXIO + tristate "iNexio serial touchscreens" + select SERIO + help + Say Y here if you have an iNexio serial touchscreen connected to + your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called inexio. + config TOUCHSCREEN_MK712 tristate "ICS MicroClock MK712 touchscreen" help diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 64a11f443dce..d63bcdcd28ec 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_TOUCHSCREEN_CORGI) += corgi_ts.o obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o +obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o diff --git a/drivers/input/touchscreen/inexio.c b/drivers/input/touchscreen/inexio.c new file mode 100644 index 000000000000..192ade0a0fb9 --- /dev/null +++ b/drivers/input/touchscreen/inexio.c @@ -0,0 +1,207 @@ +/* + * iNexio serial touchscreen driver + * + * Copyright (c) 2008 Richard Lemon + * Based on the mtouch driver (c) Vojtech Pavlik and Dan Streetman + * + */ + +/* + * 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. + */ + +/* + * 2008/06/19 Richard Lemon + * Copied mtouch.c and edited for iNexio protocol + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_DESC "iNexio serial touchscreen driver" + +MODULE_AUTHOR("Richard Lemon "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + +#define INEXIO_FORMAT_TOUCH_BIT 0x01 +#define INEXIO_FORMAT_LENGTH 5 +#define INEXIO_RESPONSE_BEGIN_BYTE 0x80 + +/* todo: check specs for max length of all responses */ +#define INEXIO_MAX_LENGTH 16 + +#define INEXIO_MIN_XC 0 +#define INEXIO_MAX_XC 0x3fff +#define INEXIO_MIN_YC 0 +#define INEXIO_MAX_YC 0x3fff + +#define INEXIO_GET_XC(data) (((data[1])<<7) | data[2]) +#define INEXIO_GET_YC(data) (((data[3])<<7) | data[4]) +#define INEXIO_GET_TOUCHED(data) (INEXIO_FORMAT_TOUCH_BIT & data[0]) + +/* + * Per-touchscreen data. + */ + +struct inexio { + struct input_dev *dev; + struct serio *serio; + int idx; + unsigned char data[INEXIO_MAX_LENGTH]; + char phys[32]; +}; + +static void inexio_process_data(struct inexio *pinexio) +{ + struct input_dev *dev = pinexio->dev; + + if (INEXIO_FORMAT_LENGTH == ++pinexio->idx) { + input_report_abs(dev, ABS_X, INEXIO_GET_XC(pinexio->data)); + input_report_abs(dev, ABS_Y, INEXIO_GET_YC(pinexio->data)); + input_report_key(dev, BTN_TOUCH, INEXIO_GET_TOUCHED(pinexio->data)); + input_sync(dev); + + pinexio->idx = 0; + } +} + +static irqreturn_t inexio_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct inexio* pinexio = serio_get_drvdata(serio); + + pinexio->data[pinexio->idx] = data; + + if (INEXIO_RESPONSE_BEGIN_BYTE&pinexio->data[0]) + inexio_process_data(pinexio); + else + printk(KERN_DEBUG "inexio.c: unknown/unsynchronized data from device, byte %x\n",pinexio->data[0]); + + return IRQ_HANDLED; +} + +/* + * inexio_disconnect() is the opposite of inexio_connect() + */ + +static void inexio_disconnect(struct serio *serio) +{ + struct inexio* pinexio = serio_get_drvdata(serio); + + input_get_device(pinexio->dev); + input_unregister_device(pinexio->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(pinexio->dev); + kfree(pinexio); +} + +/* + * inexio_connect() is the routine that is called when someone adds a + * new serio device that supports iNexio protocol and registers it as + * an input device. This is usually accomplished using inputattach. + */ + +static int inexio_connect(struct serio *serio, struct serio_driver *drv) +{ + struct inexio *pinexio; + struct input_dev *input_dev; + int err; + + pinexio = kzalloc(sizeof(struct inexio), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!pinexio || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + pinexio->serio = serio; + pinexio->dev = input_dev; + snprintf(pinexio->phys, sizeof(pinexio->phys), "%s/input0", serio->phys); + + input_dev->name = "iNexio Serial TouchScreen"; + input_dev->phys = pinexio->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_INEXIO; + input_dev->id.product = 0; + input_dev->id.version = 0x0001; + input_dev->dev.parent = &serio->dev; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(pinexio->dev, ABS_X, INEXIO_MIN_XC, INEXIO_MAX_XC, 0, 0); + input_set_abs_params(pinexio->dev, ABS_Y, INEXIO_MIN_YC, INEXIO_MAX_YC, 0, 0); + + serio_set_drvdata(serio, pinexio); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(pinexio->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(pinexio); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id inexio_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_INEXIO, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, inexio_serio_ids); + +static struct serio_driver inexio_drv = { + .driver = { + .name = "inexio", + }, + .description = DRIVER_DESC, + .id_table = inexio_serio_ids, + .interrupt = inexio_interrupt, + .connect = inexio_connect, + .disconnect = inexio_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init inexio_init(void) +{ + return serio_register_driver(&inexio_drv); +} + +static void __exit inexio_exit(void) +{ + serio_unregister_driver(&inexio_drv); +} + +module_init(inexio_init); +module_exit(inexio_exit); diff --git a/include/linux/serio.h b/include/linux/serio.h index 95674d97dabd..36c6ab81c7e0 100644 --- a/include/linux/serio.h +++ b/include/linux/serio.h @@ -212,5 +212,6 @@ static inline void serio_unpin_driver(struct serio *serio) #define SERIO_TAOSEVM 0x34 #define SERIO_FUJITSU 0x35 #define SERIO_ZHENHUA 0x36 +#define SERIO_INEXIO 0x37 #endif -- cgit v1.2.3 From 80be038593dba7aa46fb24a14f0ba83e5ade0edb Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 30 Jun 2008 11:35:53 -0700 Subject: PCI: add stub for pci_set_consistent_dma_mask() When CONFIG_PCI=n, there is no stub for pci_set_consistent_dma_mask(), so add one like other similar stubs. Otherwise there can be build errors, as here: linux-next-20080630/drivers/ssb/main.c:1175: error: implicit declaration of function 'pci_set_consistent_dma_mask' Signed-off-by: Randy Dunlap Signed-off-by: Jesse Barnes --- include/linux/pci.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include/linux') diff --git a/include/linux/pci.h b/include/linux/pci.h index 76c9a4a96152..96ebaa8d80e3 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -853,6 +853,11 @@ static inline int pci_set_dma_mask(struct pci_dev *dev, u64 mask) return -EIO; } +static inline int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask) +{ + return -EIO; +} + static inline int pci_set_dma_max_seg_size(struct pci_dev *dev, unsigned int size) { -- cgit v1.2.3 From b5470dc5fc18a8ff6517c3bb538d1479e58ecb02 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 27 Jun 2008 21:44:04 -0700 Subject: md: resolve external metadata handling deadlock in md_allow_write md_allow_write() marks the metadata dirty while holding mddev->lock and then waits for the write to complete. For externally managed metadata this causes a deadlock as userspace needs to take the lock to communicate that the metadata update has completed. Change md_allow_write() in the 'external' case to start the 'mark active' operation and then return -EAGAIN. The expected side effects while waiting for userspace to write 'active' to 'array_state' are holding off reshape (code currently handles -ENOMEM), cause some 'stripe_cache_size' change requests to fail, cause some GET_BITMAP_FILE ioctl requests to fall back to GFP_NOIO, and cause updates to 'raid_disks' to fail. Except for 'stripe_cache_size' changes these failures can be mitigated by coordinating with mdmon. md_write_start() still prevents writes from occurring until the metadata handler has had a chance to take action as it unconditionally waits for MD_CHANGE_CLEAN to be cleared. [neilb@suse.de: return -EAGAIN, try GFP_NOIO] Signed-off-by: Dan Williams --- drivers/md/md.c | 27 ++++++++++++++++----------- drivers/md/raid1.c | 6 ++++-- drivers/md/raid5.c | 12 +++++++++--- include/linux/raid/md.h | 2 +- 4 files changed, 30 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/md.c b/drivers/md/md.c index df1230af02cd..43d033d9a05a 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -4172,9 +4172,11 @@ static int get_bitmap_file(mddev_t * mddev, void __user * arg) char *ptr, *buf = NULL; int err = -ENOMEM; - md_allow_write(mddev); + if (md_allow_write(mddev)) + file = kmalloc(sizeof(*file), GFP_NOIO); + else + file = kmalloc(sizeof(*file), GFP_KERNEL); - file = kmalloc(sizeof(*file), GFP_KERNEL); if (!file) goto out; @@ -5667,15 +5669,18 @@ void md_write_end(mddev_t *mddev) * may proceed without blocking. It is important to call this before * attempting a GFP_KERNEL allocation while holding the mddev lock. * Must be called with mddev_lock held. + * + * In the ->external case MD_CHANGE_CLEAN can not be cleared until mddev->lock + * is dropped, so return -EAGAIN after notifying userspace. */ -void md_allow_write(mddev_t *mddev) +int md_allow_write(mddev_t *mddev) { if (!mddev->pers) - return; + return 0; if (mddev->ro) - return; + return 0; if (!mddev->pers->sync_request) - return; + return 0; spin_lock_irq(&mddev->write_lock); if (mddev->in_sync) { @@ -5686,14 +5691,14 @@ void md_allow_write(mddev_t *mddev) mddev->safemode = 1; spin_unlock_irq(&mddev->write_lock); md_update_sb(mddev, 0); - sysfs_notify(&mddev->kobj, NULL, "array_state"); - /* wait for the dirty state to be recorded in the metadata */ - wait_event(mddev->sb_wait, - !test_bit(MD_CHANGE_CLEAN, &mddev->flags) && - !test_bit(MD_CHANGE_PENDING, &mddev->flags)); } else spin_unlock_irq(&mddev->write_lock); + + if (test_bit(MD_CHANGE_CLEAN, &mddev->flags)) + return -EAGAIN; + else + return 0; } EXPORT_SYMBOL_GPL(md_allow_write); diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index f05d5983efb6..491dc2d4ad5f 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -2136,7 +2136,7 @@ static int raid1_reshape(mddev_t *mddev) conf_t *conf = mddev_to_conf(mddev); int cnt, raid_disks; unsigned long flags; - int d, d2; + int d, d2, err; /* Cannot change chunk_size, layout, or level */ if (mddev->chunk_size != mddev->new_chunk || @@ -2148,7 +2148,9 @@ static int raid1_reshape(mddev_t *mddev) return -EINVAL; } - md_allow_write(mddev); + err = md_allow_write(mddev); + if (err) + return err; raid_disks = mddev->raid_disks + mddev->delta_disks; diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 442622067cae..8f4c70a53210 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -911,14 +911,16 @@ static int resize_stripes(raid5_conf_t *conf, int newsize) struct stripe_head *osh, *nsh; LIST_HEAD(newstripes); struct disk_info *ndisks; - int err = 0; + int err; struct kmem_cache *sc; int i; if (newsize <= conf->pool_size) return 0; /* never bother to shrink */ - md_allow_write(conf->mddev); + err = md_allow_write(conf->mddev); + if (err) + return err; /* Step 1 */ sc = kmem_cache_create(conf->cache_name[1-conf->active_name], @@ -3843,6 +3845,8 @@ raid5_store_stripe_cache_size(mddev_t *mddev, const char *page, size_t len) { raid5_conf_t *conf = mddev_to_conf(mddev); unsigned long new; + int err; + if (len >= PAGE_SIZE) return -EINVAL; if (!conf) @@ -3858,7 +3862,9 @@ raid5_store_stripe_cache_size(mddev_t *mddev, const char *page, size_t len) else break; } - md_allow_write(mddev); + err = md_allow_write(mddev); + if (err) + return err; while (new > conf->max_nr_stripes) { if (grow_one_stripe(conf)) conf->max_nr_stripes++; diff --git a/include/linux/raid/md.h b/include/linux/raid/md.h index b7386ae9d288..dc0e3fcb9f28 100644 --- a/include/linux/raid/md.h +++ b/include/linux/raid/md.h @@ -95,7 +95,7 @@ extern int sync_page_io(struct block_device *bdev, sector_t sector, int size, struct page *page, int rw); extern void md_do_sync(mddev_t *mddev); extern void md_new_event(mddev_t *mddev); -extern void md_allow_write(mddev_t *mddev); +extern int md_allow_write(mddev_t *mddev); extern void md_wait_for_blocked_rdev(mdk_rdev_t *rdev, mddev_t *mddev); #endif /* CONFIG_MD */ -- cgit v1.2.3 From 0853ad66b14feb12acde7ac13b7c3b75770a0adc Mon Sep 17 00:00:00 2001 From: Santwona Behera Date: Wed, 2 Jul 2008 03:47:41 -0700 Subject: netdev: Add support for rx flow hash configuration, using ethtool. Added new interfaces to ethtool to configure receive network flow distribution across multiple rx rings using hashing. Signed-off-by: Santwona Behera Signed-off-by: David S. Miller --- include/linux/ethtool.h | 33 +++++++++++++++++++++++++++++++++ net/core/ethtool.c | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) (limited to 'include/linux') diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index c8d216357865..8bb5e87df365 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -272,6 +272,12 @@ enum ethtool_flags { ETH_FLAG_LRO = (1 << 15), /* LRO is enabled */ }; +struct ethtool_rxnfc { + __u32 cmd; + __u32 flow_type; + __u64 data; +}; + #ifdef __KERNEL__ struct net_device; @@ -396,6 +402,8 @@ struct ethtool_ops { /* the following hooks are obsolete */ int (*self_test_count)(struct net_device *);/* use get_sset_count */ int (*get_stats_count)(struct net_device *);/* use get_sset_count */ + int (*get_rxhash)(struct net_device *, struct ethtool_rxnfc *); + int (*set_rxhash)(struct net_device *, struct ethtool_rxnfc *); }; #endif /* __KERNEL__ */ @@ -442,6 +450,9 @@ struct ethtool_ops { #define ETHTOOL_GPFLAGS 0x00000027 /* Get driver-private flags bitmap */ #define ETHTOOL_SPFLAGS 0x00000028 /* Set driver-private flags bitmap */ +#define ETHTOOL_GRXFH 0x00000029 /* Get RX flow hash configuration */ +#define ETHTOOL_SRXFH 0x0000002a /* Set RX flow hash configuration */ + /* compatibility with older code */ #define SPARC_ETH_GSET ETHTOOL_GSET #define SPARC_ETH_SSET ETHTOOL_SSET @@ -528,4 +539,26 @@ struct ethtool_ops { #define WAKE_MAGIC (1 << 5) #define WAKE_MAGICSECURE (1 << 6) /* only meaningful if WAKE_MAGIC */ +/* L3-L4 network traffic flow types */ +#define TCP_V4_FLOW 0x01 +#define UDP_V4_FLOW 0x02 +#define SCTP_V4_FLOW 0x03 +#define AH_ESP_V4_FLOW 0x04 +#define TCP_V6_FLOW 0x05 +#define UDP_V6_FLOW 0x06 +#define SCTP_V6_FLOW 0x07 +#define AH_ESP_V6_FLOW 0x08 + +/* L3-L4 network traffic flow hash options */ +#define RXH_DEV_PORT (1 << 0) +#define RXH_L2DA (1 << 1) +#define RXH_VLAN (1 << 2) +#define RXH_L3_PROTO (1 << 3) +#define RXH_IP_SRC (1 << 4) +#define RXH_IP_DST (1 << 5) +#define RXH_L4_B_0_1 (1 << 6) /* src port in case of TCP/UDP/SCTP */ +#define RXH_L4_B_2_3 (1 << 7) /* dst port in case of TCP/UDP/SCTP */ +#define RXH_DISCARD (1 << 31) + + #endif /* _LINUX_ETHTOOL_H */ diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 0133b5ebd545..14ada537f895 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -209,6 +209,36 @@ static int ethtool_get_drvinfo(struct net_device *dev, void __user *useraddr) return 0; } +static int ethtool_set_rxhash(struct net_device *dev, void __user *useraddr) +{ + struct ethtool_rxnfc cmd; + + if (!dev->ethtool_ops->set_rxhash) + return -EOPNOTSUPP; + + if (copy_from_user(&cmd, useraddr, sizeof(cmd))) + return -EFAULT; + + return dev->ethtool_ops->set_rxhash(dev, &cmd); +} + +static int ethtool_get_rxhash(struct net_device *dev, void __user *useraddr) +{ + struct ethtool_rxnfc info; + + if (!dev->ethtool_ops->get_rxhash) + return -EOPNOTSUPP; + + if (copy_from_user(&info, useraddr, sizeof(info))) + return -EFAULT; + + dev->ethtool_ops->get_rxhash(dev, &info); + + if (copy_to_user(useraddr, &info, sizeof(info))) + return -EFAULT; + return 0; +} + static int ethtool_get_regs(struct net_device *dev, char __user *useraddr) { struct ethtool_regs regs; @@ -826,6 +856,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_GGSO: case ETHTOOL_GFLAGS: case ETHTOOL_GPFLAGS: + case ETHTOOL_GRXFH: break; default: if (!capable(CAP_NET_ADMIN)) @@ -977,6 +1008,12 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) rc = ethtool_set_value(dev, useraddr, dev->ethtool_ops->set_priv_flags); break; + case ETHTOOL_GRXFH: + rc = ethtool_get_rxhash(dev, useraddr); + break; + case ETHTOOL_SRXFH: + rc = ethtool_set_rxhash(dev, useraddr); + break; default: rc = -EOPNOTSUPP; } -- cgit v1.2.3 From ab96dddbedf4bb8a7a0fe44012efc1d99598c36f Mon Sep 17 00:00:00 2001 From: Tom Tucker Date: Wed, 28 May 2008 13:54:04 -0500 Subject: svcrdma: Add a type for keeping NFS RPC mapping Create a new data structure to hold the remote client address space to local server address space mapping. Signed-off-by: Tom Tucker --- include/linux/sunrpc/svc_rdma.h | 27 +++++++++++++++++++++++++++ net/sunrpc/xprtrdma/svc_rdma.c | 19 +++++++++++++++++++ net/sunrpc/xprtrdma/svc_rdma_transport.c | 26 ++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index 05eb4664d0dd..bd8749cc8084 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -86,6 +86,31 @@ struct svc_rdma_op_ctxt { struct page *pages[RPCSVC_MAXPAGES]; }; +/* + * NFS_ requests are mapped on the client side by the chunk lists in + * the RPCRDMA header. During the fetching of the RPC from the client + * and the writing of the reply to the client, the memory in the + * client and the memory in the server must be mapped as contiguous + * vaddr/len for access by the hardware. These data strucures keep + * these mappings. + * + * For an RDMA_WRITE, the 'sge' maps the RPC REPLY. For RDMA_READ, the + * 'sge' in the svc_rdma_req_map maps the server side RPC reply and the + * 'ch' field maps the read-list of the RPCRDMA header to the 'sge' + * mapping of the reply. + */ +struct svc_rdma_chunk_sge { + int start; /* sge no for this chunk */ + int count; /* sge count for this chunk */ +}; +struct svc_rdma_req_map { + unsigned long count; + union { + struct kvec sge[RPCSVC_MAXPAGES]; + struct svc_rdma_chunk_sge ch[RPCSVC_MAXPAGES]; + }; +}; + #define RDMACTXT_F_LAST_CTXT 2 struct svcxprt_rdma { @@ -173,6 +198,8 @@ extern int svc_rdma_post_recv(struct svcxprt_rdma *); extern int svc_rdma_create_listen(struct svc_serv *, int, struct sockaddr *); extern struct svc_rdma_op_ctxt *svc_rdma_get_context(struct svcxprt_rdma *); extern void svc_rdma_put_context(struct svc_rdma_op_ctxt *, int); +extern struct svc_rdma_req_map *svc_rdma_get_req_map(void); +extern void svc_rdma_put_req_map(struct svc_rdma_req_map *); extern void svc_sq_reap(struct svcxprt_rdma *); extern void svc_rq_reap(struct svcxprt_rdma *); extern struct svc_xprt_class svc_rdma_class; diff --git a/net/sunrpc/xprtrdma/svc_rdma.c b/net/sunrpc/xprtrdma/svc_rdma.c index 88c0ca20bb1e..171f2053e90c 100644 --- a/net/sunrpc/xprtrdma/svc_rdma.c +++ b/net/sunrpc/xprtrdma/svc_rdma.c @@ -69,6 +69,9 @@ atomic_t rdma_stat_rq_prod; atomic_t rdma_stat_sq_poll; atomic_t rdma_stat_sq_prod; +/* Temporary NFS request map cache */ +struct kmem_cache *svc_rdma_map_cachep; + /* * This function implements reading and resetting an atomic_t stat * variable through read/write to a proc file. Any write to the file @@ -241,6 +244,7 @@ void svc_rdma_cleanup(void) svcrdma_table_header = NULL; } svc_unreg_xprt_class(&svc_rdma_class); + kmem_cache_destroy(svc_rdma_map_cachep); } int svc_rdma_init(void) @@ -255,9 +259,24 @@ int svc_rdma_init(void) svcrdma_table_header = register_sysctl_table(svcrdma_root_table); + /* Create the temporary map cache */ + svc_rdma_map_cachep = kmem_cache_create("svc_rdma_map_cache", + sizeof(struct svc_rdma_req_map), + 0, + SLAB_HWCACHE_ALIGN, + NULL); + if (!svc_rdma_map_cachep) { + printk(KERN_INFO "Could not allocate map cache.\n"); + goto err; + } + /* Register RDMA with the SVC transport switch */ svc_reg_xprt_class(&svc_rdma_class); return 0; + + err: + unregister_sysctl_table(svcrdma_table_header); + return -ENOMEM; } MODULE_AUTHOR("Tom Tucker "); MODULE_DESCRIPTION("SVC RDMA Transport"); diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index e132509d1db0..ae90758d8e9b 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -173,6 +173,32 @@ void svc_rdma_put_context(struct svc_rdma_op_ctxt *ctxt, int free_pages) atomic_dec(&xprt->sc_ctxt_used); } +/* Temporary NFS request map cache. Created in svc_rdma.c */ +extern struct kmem_cache *svc_rdma_map_cachep; + +/* + * Temporary NFS req mappings are shared across all transport + * instances. These are short lived and should be bounded by the number + * of concurrent server threads * depth of the SQ. + */ +struct svc_rdma_req_map *svc_rdma_get_req_map(void) +{ + struct svc_rdma_req_map *map; + while (1) { + map = kmem_cache_alloc(svc_rdma_map_cachep, GFP_KERNEL); + if (map) + break; + schedule_timeout_uninterruptible(msecs_to_jiffies(500)); + } + map->count = 0; + return map; +} + +void svc_rdma_put_req_map(struct svc_rdma_req_map *map) +{ + kmem_cache_free(svc_rdma_map_cachep, map); +} + /* ib_cq event handler */ static void cq_event_handler(struct ib_event *event, void *context) { -- cgit v1.2.3 From f820c57ebf5493d4602cc00577c8b0fadd27a7b8 Mon Sep 17 00:00:00 2001 From: Tom Tucker Date: Tue, 27 May 2008 17:03:14 -0500 Subject: svcrdma: Use reply and chunk map for RDMA_READ processing Modify the RDMA_READ processing to use the reply and chunk list mapping data types. Also add a special purpose 'hdr_count' field in in the context to hold the header page count instead of overloading the SGE length field and corrupting the DMA map length. Signed-off-by: Tom Tucker --- include/linux/sunrpc/svc_rdma.h | 1 + net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 83 +++++++++++++++------------------ 2 files changed, 39 insertions(+), 45 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index bd8749cc8084..fd5e8a1c17de 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -72,6 +72,7 @@ extern atomic_t rdma_stat_sq_prod; */ struct svc_rdma_op_ctxt { struct svc_rdma_op_ctxt *read_hdr; + int hdr_count; struct list_head free_list; struct xdr_buf arg; struct list_head dto_q; diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index 06ab4841537b..d25971b42a74 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c @@ -112,11 +112,6 @@ static void rdma_build_arg_xdr(struct svc_rqst *rqstp, rqstp->rq_arg.tail[0].iov_len = 0; } -struct chunk_sge { - int start; /* sge no for this chunk */ - int count; /* sge count for this chunk */ -}; - /* Encode a read-chunk-list as an array of IB SGE * * Assumptions: @@ -134,8 +129,8 @@ static int rdma_rcl_to_sge(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp, struct svc_rdma_op_ctxt *head, struct rpcrdma_msg *rmsgp, - struct ib_sge *sge, - struct chunk_sge *ch_sge_ary, + struct svc_rdma_req_map *rpl_map, + struct svc_rdma_req_map *chl_map, int ch_count, int byte_count) { @@ -156,22 +151,18 @@ static int rdma_rcl_to_sge(struct svcxprt_rdma *xprt, head->arg.head[0] = rqstp->rq_arg.head[0]; head->arg.tail[0] = rqstp->rq_arg.tail[0]; head->arg.pages = &head->pages[head->count]; - head->sge[0].length = head->count; /* save count of hdr pages */ + head->hdr_count = head->count; /* save count of hdr pages */ head->arg.page_base = 0; head->arg.page_len = ch_bytes; head->arg.len = rqstp->rq_arg.len + ch_bytes; head->arg.buflen = rqstp->rq_arg.buflen + ch_bytes; head->count++; - ch_sge_ary[0].start = 0; + chl_map->ch[0].start = 0; while (byte_count) { + rpl_map->sge[sge_no].iov_base = + page_address(rqstp->rq_arg.pages[page_no]) + page_off; sge_bytes = min_t(int, PAGE_SIZE-page_off, ch_bytes); - sge[sge_no].addr = - ib_dma_map_page(xprt->sc_cm_id->device, - rqstp->rq_arg.pages[page_no], - page_off, sge_bytes, - DMA_FROM_DEVICE); - sge[sge_no].length = sge_bytes; - sge[sge_no].lkey = xprt->sc_phys_mr->lkey; + rpl_map->sge[sge_no].iov_len = sge_bytes; /* * Don't bump head->count here because the same page * may be used by multiple SGE. @@ -187,11 +178,11 @@ static int rdma_rcl_to_sge(struct svcxprt_rdma *xprt, * SGE, move to the next SGE */ if (ch_bytes == 0) { - ch_sge_ary[ch_no].count = - sge_no - ch_sge_ary[ch_no].start; + chl_map->ch[ch_no].count = + sge_no - chl_map->ch[ch_no].start; ch_no++; ch++; - ch_sge_ary[ch_no].start = sge_no; + chl_map->ch[ch_no].start = sge_no; ch_bytes = ch->rc_target.rs_length; /* If bytes remaining account for next chunk */ if (byte_count) { @@ -220,18 +211,24 @@ static int rdma_rcl_to_sge(struct svcxprt_rdma *xprt, return sge_no; } -static void rdma_set_ctxt_sge(struct svc_rdma_op_ctxt *ctxt, - struct ib_sge *sge, +static void rdma_set_ctxt_sge(struct svcxprt_rdma *xprt, + struct svc_rdma_op_ctxt *ctxt, + struct kvec *vec, u64 *sgl_offset, int count) { int i; ctxt->count = count; + ctxt->direction = DMA_FROM_DEVICE; for (i = 0; i < count; i++) { - ctxt->sge[i].addr = sge[i].addr; - ctxt->sge[i].length = sge[i].length; - *sgl_offset = *sgl_offset + sge[i].length; + ctxt->sge[i].addr = + ib_dma_map_single(xprt->sc_cm_id->device, + vec[i].iov_base, vec[i].iov_len, + DMA_FROM_DEVICE); + ctxt->sge[i].length = vec[i].iov_len; + ctxt->sge[i].lkey = xprt->sc_phys_mr->lkey; + *sgl_offset = *sgl_offset + vec[i].iov_len; } } @@ -282,34 +279,29 @@ static int rdma_read_xdr(struct svcxprt_rdma *xprt, struct ib_send_wr read_wr; int err = 0; int ch_no; - struct ib_sge *sge; int ch_count; int byte_count; int sge_count; u64 sgl_offset; struct rpcrdma_read_chunk *ch; struct svc_rdma_op_ctxt *ctxt = NULL; - struct svc_rdma_op_ctxt *tmp_sge_ctxt; - struct svc_rdma_op_ctxt *tmp_ch_ctxt; - struct chunk_sge *ch_sge_ary; + struct svc_rdma_req_map *rpl_map; + struct svc_rdma_req_map *chl_map; /* If no read list is present, return 0 */ ch = svc_rdma_get_read_chunk(rmsgp); if (!ch) return 0; - /* Allocate temporary contexts to keep SGE */ - BUG_ON(sizeof(struct ib_sge) < sizeof(struct chunk_sge)); - tmp_sge_ctxt = svc_rdma_get_context(xprt); - sge = tmp_sge_ctxt->sge; - tmp_ch_ctxt = svc_rdma_get_context(xprt); - ch_sge_ary = (struct chunk_sge *)tmp_ch_ctxt->sge; + /* Allocate temporary reply and chunk maps */ + rpl_map = svc_rdma_get_req_map(); + chl_map = svc_rdma_get_req_map(); svc_rdma_rcl_chunk_counts(ch, &ch_count, &byte_count); if (ch_count > RPCSVC_MAXPAGES) return -EINVAL; sge_count = rdma_rcl_to_sge(xprt, rqstp, hdr_ctxt, rmsgp, - sge, ch_sge_ary, + rpl_map, chl_map, ch_count, byte_count); sgl_offset = 0; ch_no = 0; @@ -331,14 +323,15 @@ next_sge: read_wr.wr.rdma.remote_addr = get_unaligned(&(ch->rc_target.rs_offset)) + sgl_offset; - read_wr.sg_list = &sge[ch_sge_ary[ch_no].start]; + read_wr.sg_list = ctxt->sge; read_wr.num_sge = - rdma_read_max_sge(xprt, ch_sge_ary[ch_no].count); - rdma_set_ctxt_sge(ctxt, &sge[ch_sge_ary[ch_no].start], + rdma_read_max_sge(xprt, chl_map->ch[ch_no].count); + rdma_set_ctxt_sge(xprt, ctxt, + &rpl_map->sge[chl_map->ch[ch_no].start], &sgl_offset, read_wr.num_sge); if (((ch+1)->rc_discrim == 0) && - (read_wr.num_sge == ch_sge_ary[ch_no].count)) { + (read_wr.num_sge == chl_map->ch[ch_no].count)) { /* * Mark the last RDMA_READ with a bit to * indicate all RPC data has been fetched from @@ -358,9 +351,9 @@ next_sge: } atomic_inc(&rdma_stat_read); - if (read_wr.num_sge < ch_sge_ary[ch_no].count) { - ch_sge_ary[ch_no].count -= read_wr.num_sge; - ch_sge_ary[ch_no].start += read_wr.num_sge; + if (read_wr.num_sge < chl_map->ch[ch_no].count) { + chl_map->ch[ch_no].count -= read_wr.num_sge; + chl_map->ch[ch_no].start += read_wr.num_sge; goto next_sge; } sgl_offset = 0; @@ -368,8 +361,8 @@ next_sge: } out: - svc_rdma_put_context(tmp_sge_ctxt, 0); - svc_rdma_put_context(tmp_ch_ctxt, 0); + svc_rdma_put_req_map(rpl_map); + svc_rdma_put_req_map(chl_map); /* Detach arg pages. svc_recv will replenish them */ for (ch_no = 0; &rqstp->rq_pages[ch_no] < rqstp->rq_respages; ch_no++) @@ -399,7 +392,7 @@ static int rdma_read_complete(struct svc_rqst *rqstp, rqstp->rq_pages[page_no] = head->pages[page_no]; } /* Point rq_arg.pages past header */ - rqstp->rq_arg.pages = &rqstp->rq_pages[head->sge[0].length]; + rqstp->rq_arg.pages = &rqstp->rq_pages[head->hdr_count]; rqstp->rq_arg.page_len = head->arg.page_len; rqstp->rq_arg.page_base = head->arg.page_base; -- cgit v1.2.3 From 87295b6c5c7fd7bbc0ce3e7f42d2adbbac7352b9 Mon Sep 17 00:00:00 2001 From: Tom Tucker Date: Wed, 28 May 2008 13:17:44 -0500 Subject: svcrdma: Add dma map count and WARN_ON Add a dma map count in order to verify that all DMA mapping resources have been freed when the transport is closed. Signed-off-by: Tom Tucker --- include/linux/sunrpc/svc_rdma.h | 1 + net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 1 + net/sunrpc/xprtrdma/svc_rdma_sendto.c | 3 +++ net/sunrpc/xprtrdma/svc_rdma_transport.c | 5 +++++ 4 files changed, 10 insertions(+) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index fd5e8a1c17de..ab93afc03c43 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -130,6 +130,7 @@ struct svcxprt_rdma { struct ib_pd *sc_pd; + atomic_t sc_dma_used; atomic_t sc_ctxt_used; struct list_head sc_ctxt_free; int sc_ctxt_cnt; diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index d25971b42a74..b4b17f44cb29 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c @@ -222,6 +222,7 @@ static void rdma_set_ctxt_sge(struct svcxprt_rdma *xprt, ctxt->count = count; ctxt->direction = DMA_FROM_DEVICE; for (i = 0; i < count; i++) { + atomic_inc(&xprt->sc_dma_used); ctxt->sge[i].addr = ib_dma_map_single(xprt->sc_cm_id->device, vec[i].iov_base, vec[i].iov_len, diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c index bdc11a30e937..a19b22b452a3 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c +++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c @@ -163,6 +163,7 @@ static int send_write(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp, sge_bytes = min((size_t)bc, (size_t)(vec->sge[xdr_sge_no].iov_len-sge_off)); sge[sge_no].length = sge_bytes; + atomic_inc(&xprt->sc_dma_used); sge[sge_no].addr = ib_dma_map_single(xprt->sc_cm_id->device, (void *) @@ -385,6 +386,7 @@ static int send_reply(struct svcxprt_rdma *rdma, ctxt->count = 1; /* Prepare the SGE for the RPCRDMA Header */ + atomic_inc(&rdma->sc_dma_used); ctxt->sge[0].addr = ib_dma_map_page(rdma->sc_cm_id->device, page, 0, PAGE_SIZE, DMA_TO_DEVICE); @@ -396,6 +398,7 @@ static int send_reply(struct svcxprt_rdma *rdma, for (sge_no = 1; byte_count && sge_no < vec->count; sge_no++) { sge_bytes = min_t(size_t, vec->sge[sge_no].iov_len, byte_count); byte_count -= sge_bytes; + atomic_inc(&rdma->sc_dma_used); ctxt->sge[sge_no].addr = ib_dma_map_single(rdma->sc_cm_id->device, vec->sge[sge_no].iov_base, diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index 7e8ee66458ea..6fddd588c031 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -155,6 +155,7 @@ static void svc_rdma_unmap_dma(struct svc_rdma_op_ctxt *ctxt) struct svcxprt_rdma *xprt = ctxt->xprt; int i; for (i = 0; i < ctxt->count && ctxt->sge[i].length; i++) { + atomic_dec(&xprt->sc_dma_used); ib_dma_unmap_single(xprt->sc_cm_id->device, ctxt->sge[i].addr, ctxt->sge[i].length, @@ -519,6 +520,7 @@ static struct svcxprt_rdma *rdma_create_xprt(struct svc_serv *serv, cma_xprt->sc_max_requests = svcrdma_max_requests; cma_xprt->sc_sq_depth = svcrdma_max_requests * RPCRDMA_SQ_DEPTH_MULT; atomic_set(&cma_xprt->sc_sq_count, 0); + atomic_set(&cma_xprt->sc_ctxt_used, 0); if (!listener) { int reqs = cma_xprt->sc_max_requests; @@ -569,6 +571,7 @@ int svc_rdma_post_recv(struct svcxprt_rdma *xprt) BUG_ON(sge_no >= xprt->sc_max_sge); page = svc_rdma_get_page(); ctxt->pages[sge_no] = page; + atomic_inc(&xprt->sc_dma_used); pa = ib_dma_map_page(xprt->sc_cm_id->device, page, 0, PAGE_SIZE, DMA_FROM_DEVICE); @@ -1049,6 +1052,7 @@ static void __svc_rdma_free(struct work_struct *work) /* Warn if we leaked a resource or under-referenced */ WARN_ON(atomic_read(&rdma->sc_ctxt_used) != 0); + WARN_ON(atomic_read(&rdma->sc_dma_used) != 0); /* Destroy the QP if present (not a listener) */ if (rdma->sc_qp && !IS_ERR(rdma->sc_qp)) @@ -1169,6 +1173,7 @@ void svc_rdma_send_error(struct svcxprt_rdma *xprt, struct rpcrdma_msg *rmsgp, length = svc_rdma_xdr_encode_error(xprt, rmsgp, err, va); /* Prepare SGE for local address */ + atomic_inc(&xprt->sc_dma_used); sge.addr = ib_dma_map_page(xprt->sc_cm_id->device, p, 0, PAGE_SIZE, DMA_FROM_DEVICE); sge.lkey = xprt->sc_phys_mr->lkey; -- cgit v1.2.3 From 779a48577ba88b6a7e9748a04b0b739f36c5e6f6 Mon Sep 17 00:00:00 2001 From: Tom Tucker Date: Mon, 19 May 2008 10:17:09 -0500 Subject: svcrdma: Remove unused wait q from svcrdma_xprt structure The sc_read_wait queue head is no longer used. Remove it. Signed-off-by: Tom Tucker --- include/linux/sunrpc/svc_rdma.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index ab93afc03c43..d8d74c4ab504 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -119,7 +119,6 @@ struct svcxprt_rdma { struct rdma_cm_id *sc_cm_id; /* RDMA connection id */ struct list_head sc_accept_q; /* Conn. waiting accept */ int sc_ord; /* RDMA read limit */ - wait_queue_head_t sc_read_wait; int sc_max_sge; int sc_sq_depth; /* Depth of SQ */ -- cgit v1.2.3 From 8948896c9e098c6fd31a6a698a598a7cbd7fa40e Mon Sep 17 00:00:00 2001 From: Tom Tucker Date: Wed, 28 May 2008 15:14:02 -0500 Subject: svcrdma: Change WR context get/put to use the kmem cache Change the WR context pool to be shared across mount points. This reduces the RDMA transport memory footprint significantly since idle mounts don't consume WR context memory. Signed-off-by: Tom Tucker --- include/linux/sunrpc/svc_rdma.h | 6 -- net/sunrpc/xprtrdma/svc_rdma_transport.c | 121 +++---------------------------- 2 files changed, 12 insertions(+), 115 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index d8d74c4ab504..ef2e3a20bf3b 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -73,7 +73,6 @@ extern atomic_t rdma_stat_sq_prod; struct svc_rdma_op_ctxt { struct svc_rdma_op_ctxt *read_hdr; int hdr_count; - struct list_head free_list; struct xdr_buf arg; struct list_head dto_q; enum ib_wr_opcode wr_op; @@ -131,11 +130,6 @@ struct svcxprt_rdma { atomic_t sc_dma_used; atomic_t sc_ctxt_used; - struct list_head sc_ctxt_free; - int sc_ctxt_cnt; - int sc_ctxt_bump; - int sc_ctxt_max; - spinlock_t sc_ctxt_lock; struct list_head sc_rq_dto_q; spinlock_t sc_rq_dto_lock; struct ib_qp *sc_qp; diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index 80104f4999d5..19ddc382b777 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -84,69 +84,23 @@ struct svc_xprt_class svc_rdma_class = { .xcl_max_payload = RPCSVC_MAXPAYLOAD_TCP, }; -static int rdma_bump_context_cache(struct svcxprt_rdma *xprt) -{ - int target; - int at_least_one = 0; - struct svc_rdma_op_ctxt *ctxt; - - target = min(xprt->sc_ctxt_cnt + xprt->sc_ctxt_bump, - xprt->sc_ctxt_max); - - spin_lock_bh(&xprt->sc_ctxt_lock); - while (xprt->sc_ctxt_cnt < target) { - xprt->sc_ctxt_cnt++; - spin_unlock_bh(&xprt->sc_ctxt_lock); - - ctxt = kmalloc(sizeof(*ctxt), GFP_KERNEL); - - spin_lock_bh(&xprt->sc_ctxt_lock); - if (ctxt) { - at_least_one = 1; - INIT_LIST_HEAD(&ctxt->free_list); - list_add(&ctxt->free_list, &xprt->sc_ctxt_free); - } else { - /* kmalloc failed...give up for now */ - xprt->sc_ctxt_cnt--; - break; - } - } - spin_unlock_bh(&xprt->sc_ctxt_lock); - dprintk("svcrdma: sc_ctxt_max=%d, sc_ctxt_cnt=%d\n", - xprt->sc_ctxt_max, xprt->sc_ctxt_cnt); - return at_least_one; -} +/* WR context cache. Created in svc_rdma.c */ +extern struct kmem_cache *svc_rdma_ctxt_cachep; struct svc_rdma_op_ctxt *svc_rdma_get_context(struct svcxprt_rdma *xprt) { struct svc_rdma_op_ctxt *ctxt; while (1) { - spin_lock_bh(&xprt->sc_ctxt_lock); - if (unlikely(list_empty(&xprt->sc_ctxt_free))) { - /* Try to bump my cache. */ - spin_unlock_bh(&xprt->sc_ctxt_lock); - - if (rdma_bump_context_cache(xprt)) - continue; - - printk(KERN_INFO "svcrdma: sleeping waiting for " - "context memory on xprt=%p\n", - xprt); - schedule_timeout_uninterruptible(msecs_to_jiffies(500)); - continue; - } - ctxt = list_entry(xprt->sc_ctxt_free.next, - struct svc_rdma_op_ctxt, - free_list); - list_del_init(&ctxt->free_list); - spin_unlock_bh(&xprt->sc_ctxt_lock); - ctxt->xprt = xprt; - INIT_LIST_HEAD(&ctxt->dto_q); - ctxt->count = 0; - atomic_inc(&xprt->sc_ctxt_used); - break; + ctxt = kmem_cache_alloc(svc_rdma_ctxt_cachep, GFP_KERNEL); + if (ctxt) + break; + schedule_timeout_uninterruptible(msecs_to_jiffies(500)); } + ctxt->xprt = xprt; + INIT_LIST_HEAD(&ctxt->dto_q); + ctxt->count = 0; + atomic_inc(&xprt->sc_ctxt_used); return ctxt; } @@ -174,9 +128,7 @@ void svc_rdma_put_context(struct svc_rdma_op_ctxt *ctxt, int free_pages) for (i = 0; i < ctxt->count; i++) put_page(ctxt->pages[i]); - spin_lock_bh(&xprt->sc_ctxt_lock); - list_add(&ctxt->free_list, &xprt->sc_ctxt_free); - spin_unlock_bh(&xprt->sc_ctxt_lock); + kmem_cache_free(svc_rdma_ctxt_cachep, ctxt); atomic_dec(&xprt->sc_ctxt_used); } @@ -461,40 +413,6 @@ static void sq_comp_handler(struct ib_cq *cq, void *cq_context) tasklet_schedule(&dto_tasklet); } -static void create_context_cache(struct svcxprt_rdma *xprt, - int ctxt_count, int ctxt_bump, int ctxt_max) -{ - struct svc_rdma_op_ctxt *ctxt; - int i; - - xprt->sc_ctxt_max = ctxt_max; - xprt->sc_ctxt_bump = ctxt_bump; - xprt->sc_ctxt_cnt = 0; - atomic_set(&xprt->sc_ctxt_used, 0); - - INIT_LIST_HEAD(&xprt->sc_ctxt_free); - for (i = 0; i < ctxt_count; i++) { - ctxt = kmalloc(sizeof(*ctxt), GFP_KERNEL); - if (ctxt) { - INIT_LIST_HEAD(&ctxt->free_list); - list_add(&ctxt->free_list, &xprt->sc_ctxt_free); - xprt->sc_ctxt_cnt++; - } - } -} - -static void destroy_context_cache(struct svcxprt_rdma *xprt) -{ - while (!list_empty(&xprt->sc_ctxt_free)) { - struct svc_rdma_op_ctxt *ctxt; - ctxt = list_entry(xprt->sc_ctxt_free.next, - struct svc_rdma_op_ctxt, - free_list); - list_del_init(&ctxt->free_list); - kfree(ctxt); - } -} - static struct svcxprt_rdma *rdma_create_xprt(struct svc_serv *serv, int listener) { @@ -511,7 +429,6 @@ static struct svcxprt_rdma *rdma_create_xprt(struct svc_serv *serv, spin_lock_init(&cma_xprt->sc_lock); spin_lock_init(&cma_xprt->sc_read_complete_lock); - spin_lock_init(&cma_xprt->sc_ctxt_lock); spin_lock_init(&cma_xprt->sc_rq_dto_lock); cma_xprt->sc_ord = svcrdma_ord; @@ -522,20 +439,7 @@ static struct svcxprt_rdma *rdma_create_xprt(struct svc_serv *serv, atomic_set(&cma_xprt->sc_sq_count, 0); atomic_set(&cma_xprt->sc_ctxt_used, 0); - if (!listener) { - int reqs = cma_xprt->sc_max_requests; - create_context_cache(cma_xprt, - reqs << 1, /* starting size */ - reqs, /* bump amount */ - reqs + - cma_xprt->sc_sq_depth + - RPCRDMA_MAX_THREADS + 1); /* max */ - if (list_empty(&cma_xprt->sc_ctxt_free)) { - kfree(cma_xprt); - return NULL; - } - clear_bit(XPT_LISTENER, &cma_xprt->sc_xprt.xpt_flags); - } else + if (listener) set_bit(XPT_LISTENER, &cma_xprt->sc_xprt.xpt_flags); return cma_xprt; @@ -1077,7 +981,6 @@ static void __svc_rdma_free(struct work_struct *work) /* Destroy the CM ID */ rdma_destroy_id(rdma->sc_cm_id); - destroy_context_cache(rdma); kfree(rdma); } -- cgit v1.2.3 From 778d80be52699596bf70e0eb0761cf5e1e46088d Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki Date: Sat, 28 Jun 2008 14:17:11 +0900 Subject: ipv6: Add disable_ipv6 sysctl to disable IPv6 operaion on specific interface. Signed-off-by: YOSHIFUJI Hideaki --- Documentation/networking/ip-sysctl.txt | 4 ++++ include/linux/ipv6.h | 2 ++ net/ipv6/addrconf.c | 11 +++++++++++ net/ipv6/ip6_input.c | 3 ++- net/ipv6/ip6_output.c | 7 +++++++ 5 files changed, 26 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 71c7bea97160..dae980e8f1b9 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -1025,6 +1025,10 @@ max_addresses - INTEGER autoconfigured addresses. Default: 16 +disable_ipv6 - BOOLEAN + Disable IPv6 operation. + Default: FALSE (enable IPv6 operation) + icmp/*: ratelimit - INTEGER Limit the maximal rates for sending ICMPv6 packets. diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index cde056e08181..d9d7f9b69eb4 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -163,6 +163,7 @@ struct ipv6_devconf { #ifdef CONFIG_IPV6_MROUTE __s32 mc_forwarding; #endif + __s32 disable_ipv6; void *sysctl; }; @@ -194,6 +195,7 @@ enum { DEVCONF_OPTIMISTIC_DAD, DEVCONF_ACCEPT_SOURCE_ROUTE, DEVCONF_MC_FORWARDING, + DEVCONF_DISABLE_IPV6, DEVCONF_MAX }; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 8b6875f02039..8c5cff50bbed 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -183,6 +183,7 @@ struct ipv6_devconf ipv6_devconf __read_mostly = { #endif .proxy_ndp = 0, .accept_source_route = 0, /* we do not accept RH0 by default. */ + .disable_ipv6 = 0, }; static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { @@ -215,6 +216,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { #endif .proxy_ndp = 0, .accept_source_route = 0, /* we do not accept RH0 by default. */ + .disable_ipv6 = 0, }; /* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */ @@ -3657,6 +3659,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, #ifdef CONFIG_IPV6_MROUTE array[DEVCONF_MC_FORWARDING] = cnf->mc_forwarding; #endif + array[DEVCONF_DISABLE_IPV6] = cnf->disable_ipv6; } static inline size_t inet6_if_nlmsg_size(void) @@ -4215,6 +4218,14 @@ static struct addrconf_sysctl_table .proc_handler = &proc_dointvec, }, #endif + { + .ctl_name = CTL_UNNUMBERED, + .procname = "disable_ipv6", + .data = &ipv6_devconf.disable_ipv6, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, { .ctl_name = 0, /* sentinel */ } diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index 34e5a96623ae..ea81c614dde2 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -71,7 +71,8 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt IP6_INC_STATS_BH(idev, IPSTATS_MIB_INRECEIVES); - if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) { + if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL || + !idev || unlikely(idev->cnf.disable_ipv6)) { IP6_INC_STATS_BH(idev, IPSTATS_MIB_INDISCARDS); rcu_read_unlock(); goto out; diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 871bdec09edb..0981c1ef3057 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -173,6 +173,13 @@ static inline int ip6_skb_dst_mtu(struct sk_buff *skb) int ip6_output(struct sk_buff *skb) { + struct inet6_dev *idev = ip6_dst_idev(skb->dst); + if (unlikely(idev->cnf.disable_ipv6)) { + IP6_INC_STATS(idev, IPSTATS_MIB_OUTDISCARDS); + kfree_skb(skb); + return 0; + } + if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) || dst_allfrag(skb->dst)) return ip6_fragment(skb, ip6_output2); -- cgit v1.2.3 From 1b34be74cbf18f5d58cc85c7c4afcd9f7d74accd Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki Date: Sat, 28 Jun 2008 14:18:38 +0900 Subject: ipv6 addrconf: add accept_dad sysctl to control DAD operation. - If 0, disable DAD. - If 1, perform DAD (default). - If >1, perform DAD and disable IPv6 operation if DAD for MAC-based link-local address has been failed (RFC4862 5.4.5). We do not follow RFC4862 by default. Refer to the netdev thread entitled "Linux IPv6 DAD not full conform to RFC 4862 ?" http://www.spinics.net/lists/netdev/msg52027.html Signed-off-by: YOSHIFUJI Hideaki --- Documentation/networking/ip-sysctl.txt | 7 +++++++ include/linux/ipv6.h | 2 ++ net/ipv6/addrconf.c | 35 ++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+) (limited to 'include/linux') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index dae980e8f1b9..72f6d52e52e6 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -1029,6 +1029,13 @@ disable_ipv6 - BOOLEAN Disable IPv6 operation. Default: FALSE (enable IPv6 operation) +accept_dad - INTEGER + Whether to accept DAD (Duplicate Address Detection). + 0: Disable DAD + 1: Enable DAD (default) + 2: Enable DAD, and disable IPv6 operation if MAC-based duplicate + link-local address has been found. + icmp/*: ratelimit - INTEGER Limit the maximal rates for sending ICMPv6 packets. diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index d9d7f9b69eb4..391ad0843a46 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -164,6 +164,7 @@ struct ipv6_devconf { __s32 mc_forwarding; #endif __s32 disable_ipv6; + __s32 accept_dad; void *sysctl; }; @@ -196,6 +197,7 @@ enum { DEVCONF_ACCEPT_SOURCE_ROUTE, DEVCONF_MC_FORWARDING, DEVCONF_DISABLE_IPV6, + DEVCONF_ACCEPT_DAD, DEVCONF_MAX }; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 8c5cff50bbed..2ec73e62202c 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -119,6 +119,7 @@ static void ipv6_regen_rndid(unsigned long data); static int desync_factor = MAX_DESYNC_FACTOR * HZ; #endif +static int ipv6_generate_eui64(u8 *eui, struct net_device *dev); static int ipv6_count_addresses(struct inet6_dev *idev); /* @@ -184,6 +185,7 @@ struct ipv6_devconf ipv6_devconf __read_mostly = { .proxy_ndp = 0, .accept_source_route = 0, /* we do not accept RH0 by default. */ .disable_ipv6 = 0, + .accept_dad = 1, }; static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { @@ -217,6 +219,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { .proxy_ndp = 0, .accept_source_route = 0, /* we do not accept RH0 by default. */ .disable_ipv6 = 0, + .accept_dad = 1, }; /* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */ @@ -380,6 +383,9 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev) */ in6_dev_hold(ndev); + if (dev->flags & (IFF_NOARP | IFF_LOOPBACK)) + ndev->cnf.accept_dad = -1; + #if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE) if (dev->type == ARPHRD_SIT && (dev->priv_flags & IFF_ISATAP)) { printk(KERN_INFO @@ -1421,6 +1427,20 @@ static void addrconf_dad_stop(struct inet6_ifaddr *ifp) void addrconf_dad_failure(struct inet6_ifaddr *ifp) { + struct inet6_dev *idev = ifp->idev; + if (idev->cnf.accept_dad > 1 && !idev->cnf.disable_ipv6) { + struct in6_addr addr; + + addr.s6_addr32[0] = htonl(0xfe800000); + addr.s6_addr32[1] = 0; + + if (!ipv6_generate_eui64(addr.s6_addr + 8, idev->dev) && + ipv6_addr_equal(&ifp->addr, &addr)) { + /* DAD failed for link-local based on MAC address */ + idev->cnf.disable_ipv6 = 1; + } + } + if (net_ratelimit()) printk(KERN_INFO "%s: duplicate address detected!\n", ifp->idev->dev->name); addrconf_dad_stop(ifp); @@ -2753,6 +2773,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags) spin_lock_bh(&ifp->lock); if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) || + idev->cnf.accept_dad < 1 || !(ifp->flags&IFA_F_TENTATIVE) || ifp->flags & IFA_F_NODAD) { ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC); @@ -2800,6 +2821,11 @@ static void addrconf_dad_timer(unsigned long data) read_unlock_bh(&idev->lock); goto out; } + if (idev->cnf.accept_dad > 1 && idev->cnf.disable_ipv6) { + read_unlock_bh(&idev->lock); + addrconf_dad_failure(ifp); + return; + } spin_lock_bh(&ifp->lock); if (ifp->probes == 0) { /* @@ -3660,6 +3686,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, array[DEVCONF_MC_FORWARDING] = cnf->mc_forwarding; #endif array[DEVCONF_DISABLE_IPV6] = cnf->disable_ipv6; + array[DEVCONF_ACCEPT_DAD] = cnf->accept_dad; } static inline size_t inet6_if_nlmsg_size(void) @@ -4226,6 +4253,14 @@ static struct addrconf_sysctl_table .mode = 0644, .proc_handler = &proc_dointvec, }, + { + .ctl_name = CTL_UNNUMBERED, + .procname = "accept_dad", + .data = &ipv6_devconf.accept_dad, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, { .ctl_name = 0, /* sentinel */ } -- cgit v1.2.3 From 623d1a1af77bd52a389c6eda5920e28eb2ee468b Mon Sep 17 00:00:00 2001 From: Wang Chen Date: Thu, 3 Jul 2008 12:13:30 +0800 Subject: ipv6: Do cleanup for ip6_mr_init. If do not do it, we will get following issues: 1. Leaving junks after inet6_init failing halfway. 2. Leaving proc and notifier junks after ipv6 modules unloading. Signed-off-by: Wang Chen Signed-off-by: YOSHIFUJI Hideaki --- include/linux/mroute6.h | 3 ++- net/ipv6/af_inet6.c | 11 ++++++++++- net/ipv6/ip6mr.c | 38 +++++++++++++++++++++++++++++++++----- 3 files changed, 45 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mroute6.h b/include/linux/mroute6.h index e7989593142b..4c4d6f57d5c5 100644 --- a/include/linux/mroute6.h +++ b/include/linux/mroute6.h @@ -135,7 +135,8 @@ extern int ip6_mroute_setsockopt(struct sock *, int, char __user *, int); extern int ip6_mroute_getsockopt(struct sock *, int, char __user *, int __user *); extern int ip6_mr_input(struct sk_buff *skb); extern int ip6mr_ioctl(struct sock *sk, int cmd, void __user *arg); -extern void ip6_mr_init(void); +extern int ip6_mr_init(void); +extern void ip6_mr_cleanup(void); struct mif_device { diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 3ce8d2f318c6..6b39af1acb5e 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -953,7 +953,9 @@ static int __init inet6_init(void) if (err) goto icmp_fail; #ifdef CONFIG_IPV6_MROUTE - ip6_mr_init(); + err = ip6_mr_init(); + if (err) + goto ipmr_fail; #endif err = ndisc_init(); if (err) @@ -1057,6 +1059,10 @@ netfilter_fail: igmp_fail: ndisc_cleanup(); ndisc_fail: +#ifdef CONFIG_IPV6_MROUTE + ip6_mr_cleanup(); +ipmr_fail: +#endif icmpv6_cleanup(); icmp_fail: unregister_pernet_subsys(&inet6_net_ops); @@ -1111,6 +1117,9 @@ static void __exit inet6_exit(void) ipv6_netfilter_fini(); igmp6_cleanup(); ndisc_cleanup(); +#ifdef CONFIG_IPV6_MROUTE + ip6_mr_cleanup(); +#endif icmpv6_cleanup(); rawv6_exit(); diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 90e763073dc5..cfac26d674ed 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -948,23 +948,51 @@ static struct notifier_block ip6_mr_notifier = { * Setup for IP multicast routing */ -void __init ip6_mr_init(void) +int __init ip6_mr_init(void) { + int err; + mrt_cachep = kmem_cache_create("ip6_mrt_cache", sizeof(struct mfc6_cache), 0, SLAB_HWCACHE_ALIGN, NULL); if (!mrt_cachep) - panic("cannot allocate ip6_mrt_cache"); + return -ENOMEM; setup_timer(&ipmr_expire_timer, ipmr_expire_process, 0); - register_netdevice_notifier(&ip6_mr_notifier); + err = register_netdevice_notifier(&ip6_mr_notifier); + if (err) + goto reg_notif_fail; +#ifdef CONFIG_PROC_FS + err = -ENOMEM; + if (!proc_net_fops_create(&init_net, "ip6_mr_vif", 0, &ip6mr_vif_fops)) + goto proc_vif_fail; + if (!proc_net_fops_create(&init_net, "ip6_mr_cache", + 0, &ip6mr_mfc_fops)) + goto proc_cache_fail; +#endif + return 0; +reg_notif_fail: + kmem_cache_destroy(mrt_cachep); #ifdef CONFIG_PROC_FS - proc_net_fops_create(&init_net, "ip6_mr_vif", 0, &ip6mr_vif_fops); - proc_net_fops_create(&init_net, "ip6_mr_cache", 0, &ip6mr_mfc_fops); +proc_vif_fail: + unregister_netdevice_notifier(&ip6_mr_notifier); +proc_cache_fail: + proc_net_remove(&init_net, "ip6_mr_vif"); #endif + return err; } +void ip6_mr_cleanup(void) +{ +#ifdef CONFIG_PROC_FS + proc_net_remove(&init_net, "ip6_mr_cache"); + proc_net_remove(&init_net, "ip6_mr_vif"); +#endif + unregister_netdevice_notifier(&ip6_mr_notifier); + del_timer(&ipmr_expire_timer); + kmem_cache_destroy(mrt_cachep); +} static int ip6mr_mfc_add(struct mf6cctl *mfc, int mrtsock) { -- cgit v1.2.3 From 03d2f897e9fb3218989baa2139a951ce7f5414bf Mon Sep 17 00:00:00 2001 From: Wang Chen Date: Thu, 3 Jul 2008 12:13:36 +0800 Subject: ipv4: Do cleanup for ip_mr_init Same as ip6_mr_init(), make ip_mr_init() return errno if fails. But do not do error handling in inet_init(), just print a msg. Signed-off-by: Wang Chen Signed-off-by: YOSHIFUJI Hideaki --- include/linux/igmp.h | 1 - include/linux/mroute.h | 3 +-- net/ipv4/af_inet.c | 5 +++-- net/ipv4/ipmr.c | 28 ++++++++++++++++++++++++---- 4 files changed, 28 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/include/linux/igmp.h b/include/linux/igmp.h index f5a1a0db2e8e..7bb3c095c15b 100644 --- a/include/linux/igmp.h +++ b/include/linux/igmp.h @@ -228,7 +228,6 @@ extern int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf, extern int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf, struct group_filter __user *optval, int __user *optlen); extern int ip_mc_sf_allow(struct sock *sk, __be32 local, __be32 rmt, int dif); -extern void ip_mr_init(void); extern void ip_mc_init_dev(struct in_device *); extern void ip_mc_destroy_dev(struct in_device *); extern void ip_mc_up(struct in_device *); diff --git a/include/linux/mroute.h b/include/linux/mroute.h index de4decfa1bfc..df8efd42bf8a 100644 --- a/include/linux/mroute.h +++ b/include/linux/mroute.h @@ -147,8 +147,7 @@ static inline int ip_mroute_opt(int opt) extern int ip_mroute_setsockopt(struct sock *, int, char __user *, int); extern int ip_mroute_getsockopt(struct sock *, int, char __user *, int __user *); extern int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg); -extern void ip_mr_init(void); - +extern int ip_mr_init(void); struct vif_device { diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 42bd24b64b57..dc411335c14f 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1479,14 +1479,15 @@ static int __init inet_init(void) * Initialise the multicast router */ #if defined(CONFIG_IP_MROUTE) - ip_mr_init(); + if (ip_mr_init()) + printk(KERN_CRIT "inet_init: Cannot init ipv4 mroute\n"); #endif /* * Initialise per-cpu ipv4 mibs */ if (init_ipv4_mibs()) - printk(KERN_CRIT "inet_init: Cannot init ipv4 mibs\n"); ; + printk(KERN_CRIT "inet_init: Cannot init ipv4 mibs\n"); ipv4_proc_init(); diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 300ab0c2919e..438fab9c62a0 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -1878,16 +1878,36 @@ static struct net_protocol pim_protocol = { * Setup for IP multicast routing */ -void __init ip_mr_init(void) +int __init ip_mr_init(void) { + int err; + mrt_cachep = kmem_cache_create("ip_mrt_cache", sizeof(struct mfc_cache), 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); + if (!mrt_cachep) + return -ENOMEM; + setup_timer(&ipmr_expire_timer, ipmr_expire_process, 0); - register_netdevice_notifier(&ip_mr_notifier); + err = register_netdevice_notifier(&ip_mr_notifier); + if (err) + goto reg_notif_fail; #ifdef CONFIG_PROC_FS - proc_net_fops_create(&init_net, "ip_mr_vif", 0, &ipmr_vif_fops); - proc_net_fops_create(&init_net, "ip_mr_cache", 0, &ipmr_mfc_fops); + err = -ENOMEM; + if (!proc_net_fops_create(&init_net, "ip_mr_vif", 0, &ipmr_vif_fops)) + goto proc_vif_fail; + if (!proc_net_fops_create(&init_net, "ip_mr_cache", 0, &ipmr_mfc_fops)) + goto proc_cache_fail; #endif + return 0; +reg_notif_fail: + kmem_cache_destroy(mrt_cachep); +#ifdef CONFIG_PROC_FS +proc_vif_fail: + unregister_netdevice_notifier(&ip_mr_notifier); +proc_cache_fail: + proc_net_remove(&init_net, "ip_mr_vif"); +#endif + return err; } -- cgit v1.2.3 From e0835f8fa56d2d308486f8a34cf1c4480cd27f4e Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki Date: Thu, 3 Jul 2008 16:51:22 +0900 Subject: ipv4,ipv6 mroute: Add some helper inline functions to remove ugly ifdefs. ip{,v6}_mroute_{set,get}sockopt() should not matter by optimization but it would be better not to depend on optimization semantically. Signed-off-by: YOSHIFUJI Hideaki --- include/linux/mroute.h | 27 +++++++++++++++++++++++++++ include/linux/mroute6.h | 32 ++++++++++++++++++++++++++++++++ net/ipv6/af_inet6.c | 8 -------- 3 files changed, 59 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mroute.h b/include/linux/mroute.h index df8efd42bf8a..07112ee9293a 100644 --- a/include/linux/mroute.h +++ b/include/linux/mroute.h @@ -144,10 +144,37 @@ static inline int ip_mroute_opt(int opt) } #endif +#ifdef CONFIG_IP_MROUTE extern int ip_mroute_setsockopt(struct sock *, int, char __user *, int); extern int ip_mroute_getsockopt(struct sock *, int, char __user *, int __user *); extern int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg); extern int ip_mr_init(void); +#else +static inline +int ip_mroute_setsockopt(struct sock *sock, + int optname, char __user *optval, int optlen) +{ + return -ENOPROTOOPT; +} + +static inline +int ip_mroute_getsockopt(struct sock *sock, + int optname, char __user *optval, int __user *optlen) +{ + return -ENOPROTOOPT; +} + +static inline +int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg) +{ + return -ENOIOCTLCMD; +} + +static inline int ip_mr_init(void) +{ + return 0; +} +#endif struct vif_device { diff --git a/include/linux/mroute6.h b/include/linux/mroute6.h index 4c4d6f57d5c5..5cf50473a10f 100644 --- a/include/linux/mroute6.h +++ b/include/linux/mroute6.h @@ -131,12 +131,44 @@ static inline int ip6_mroute_opt(int opt) struct sock; +#ifdef CONFIG_IPV6_MROUTE extern int ip6_mroute_setsockopt(struct sock *, int, char __user *, int); extern int ip6_mroute_getsockopt(struct sock *, int, char __user *, int __user *); extern int ip6_mr_input(struct sk_buff *skb); extern int ip6mr_ioctl(struct sock *sk, int cmd, void __user *arg); extern int ip6_mr_init(void); extern void ip6_mr_cleanup(void); +#else +static inline +int ip6_mroute_setsockopt(struct sock *sock, + int optname, char __user *optval, int optlen) +{ + return -ENOPROTOOPT; +} + +static inline +int ip6_mroute_getsockopt(struct sock *sock, + int optname, char __user *optval, int __user *optlen) +{ + return -ENOPROTOOPT; +} + +static inline +int ip6mr_ioctl(struct sock *sk, int cmd, void __user *arg) +{ + return -ENOIOCTLCMD; +} + +static inline int ip6_mr_init(void) +{ + return 0; +} + +static inline void ip6_mr_cleanup(void) +{ + return; +} +#endif struct mif_device { diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 6b39af1acb5e..3d828bc4b1cf 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -59,9 +59,7 @@ #include #include -#ifdef CONFIG_IPV6_MROUTE #include -#endif MODULE_AUTHOR("Cast of dozens"); MODULE_DESCRIPTION("IPv6 protocol stack for Linux"); @@ -952,11 +950,9 @@ static int __init inet6_init(void) err = icmpv6_init(); if (err) goto icmp_fail; -#ifdef CONFIG_IPV6_MROUTE err = ip6_mr_init(); if (err) goto ipmr_fail; -#endif err = ndisc_init(); if (err) goto ndisc_fail; @@ -1059,10 +1055,8 @@ netfilter_fail: igmp_fail: ndisc_cleanup(); ndisc_fail: -#ifdef CONFIG_IPV6_MROUTE ip6_mr_cleanup(); ipmr_fail: -#endif icmpv6_cleanup(); icmp_fail: unregister_pernet_subsys(&inet6_net_ops); @@ -1117,9 +1111,7 @@ static void __exit inet6_exit(void) ipv6_netfilter_fini(); igmp6_cleanup(); ndisc_cleanup(); -#ifdef CONFIG_IPV6_MROUTE ip6_mr_cleanup(); -#endif icmpv6_cleanup(); rawv6_exit(); -- cgit v1.2.3 From 07240fd0902c872f044f523893364a1a24c9f278 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 3 Jul 2008 03:45:32 -0700 Subject: tun: Interface to query tun/tap features. The problem with introducing checksum offload and gso to tun is they need to set dev->features to enable GSO and/or checksumming, which is supposed to be done before register_netdevice(), ie. as part of TUNSETIFF. Unfortunately, TUNSETIFF has always just ignored flags it doesn't understand, so there's no good way of detecting whether the kernel supports new IFF_ flags. This patch implements a TUNGETFEATURES ioctl which returns all the valid IFF flags. It could be extended later to include other features. Here's an example program which uses it: #include #include #include #include #include #include #include static struct { unsigned int flag; const char *name; } known_flags[] = { { IFF_TUN, "TUN" }, { IFF_TAP, "TAP" }, { IFF_NO_PI, "NO_PI" }, { IFF_ONE_QUEUE, "ONE_QUEUE" }, }; int main() { unsigned int features, i; int netfd = open("/dev/net/tun", O_RDWR); if (netfd < 0) err(1, "Opening /dev/net/tun"); if (ioctl(netfd, TUNGETFEATURES, &features) != 0) { printf("Kernel does not support TUNGETFEATURES, guessing\n"); features = (IFF_TUN|IFF_TAP|IFF_NO_PI|IFF_ONE_QUEUE); } printf("Available features are: "); for (i = 0; i < sizeof(known_flags)/sizeof(known_flags[0]); i++) { if (features & known_flags[i].flag) { features &= ~known_flags[i].flag; printf("%s ", known_flags[i].name); } } if (features) printf("(UNKNOWN %#x)", features); printf("\n"); return 0; } Signed-off-by: Rusty Russell Acked-by: Max Krasnyansky Signed-off-by: David S. Miller --- drivers/net/tun.c | 8 ++++++++ include/linux/if_tun.h | 1 + 2 files changed, 9 insertions(+) (limited to 'include/linux') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 7ab94c825b57..3bb991fd2b51 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -640,6 +640,14 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, return 0; } + if (cmd == TUNGETFEATURES) { + /* Currently this just means: "what IFF flags are valid?". + * This is needed because we never checked for invalid flags on + * TUNSETIFF. */ + return put_user(IFF_TUN | IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE, + (unsigned int __user*)argp); + } + if (!tun) return -EBADFD; diff --git a/include/linux/if_tun.h b/include/linux/if_tun.h index 18f31b6187a3..94f76a112303 100644 --- a/include/linux/if_tun.h +++ b/include/linux/if_tun.h @@ -40,6 +40,7 @@ #define TUNSETOWNER _IOW('T', 204, int) #define TUNSETLINK _IOW('T', 205, int) #define TUNSETGROUP _IOW('T', 206, int) +#define TUNGETFEATURES _IOR('T', 207, unsigned int) /* TUNSETIFF ifr flags */ #define IFF_TUN 0x0001 -- cgit v1.2.3 From 5228ddc98fa49b3cedab4024e269d62410a0d806 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 3 Jul 2008 03:46:16 -0700 Subject: tun: TUNSETFEATURES to set gso features. ethtool is useful for setting (some) device fields, but it's root-only. Finer feature control is available through a tun-specific ioctl. (Includes Mark McLoughlin 's fix to hold rtnl sem). Signed-off-by: Rusty Russell Acked-by: Max Krasnyansky Signed-off-by: David S. Miller --- drivers/net/tun.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/if_tun.h | 7 +++++++ 2 files changed, 56 insertions(+) (limited to 'include/linux') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 3bb991fd2b51..a314955e6994 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -611,6 +611,46 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) return err; } +/* This is like a cut-down ethtool ops, except done via tun fd so no + * privs required. */ +static int set_offload(struct net_device *dev, unsigned long arg) +{ + unsigned int old_features, features; + + old_features = dev->features; + /* Unset features, set them as we chew on the arg. */ + features = (old_features & ~(NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST + |NETIF_F_TSO_ECN|NETIF_F_TSO|NETIF_F_TSO6)); + + if (arg & TUN_F_CSUM) { + features |= NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST; + arg &= ~TUN_F_CSUM; + + if (arg & (TUN_F_TSO4|TUN_F_TSO6)) { + if (arg & TUN_F_TSO_ECN) { + features |= NETIF_F_TSO_ECN; + arg &= ~TUN_F_TSO_ECN; + } + if (arg & TUN_F_TSO4) + features |= NETIF_F_TSO; + if (arg & TUN_F_TSO6) + features |= NETIF_F_TSO6; + arg &= ~(TUN_F_TSO4|TUN_F_TSO6); + } + } + + /* This gives the user a way to test for new features in future by + * trying to set them. */ + if (arg) + return -EINVAL; + + dev->features = features; + if (old_features != dev->features) + netdev_features_change(dev); + + return 0; +} + static int tun_chr_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { @@ -715,6 +755,15 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, break; #endif + case TUNSETOFFLOAD: + { + int ret; + rtnl_lock(); + ret = set_offload(tun->dev, arg); + rtnl_unlock(); + return ret; + } + case SIOCGIFFLAGS: ifr.ifr_flags = tun->if_flags; if (copy_to_user( argp, &ifr, sizeof ifr)) diff --git a/include/linux/if_tun.h b/include/linux/if_tun.h index 94f76a112303..3f0a0995d449 100644 --- a/include/linux/if_tun.h +++ b/include/linux/if_tun.h @@ -41,6 +41,7 @@ #define TUNSETLINK _IOW('T', 205, int) #define TUNSETGROUP _IOW('T', 206, int) #define TUNGETFEATURES _IOR('T', 207, unsigned int) +#define TUNSETOFFLOAD _IOW('T', 208, unsigned int) /* TUNSETIFF ifr flags */ #define IFF_TUN 0x0001 @@ -48,6 +49,12 @@ #define IFF_NO_PI 0x1000 #define IFF_ONE_QUEUE 0x2000 +/* Features for GSO (TUNSETOFFLOAD). */ +#define TUN_F_CSUM 0x01 /* You can hand me unchecksummed packets. */ +#define TUN_F_TSO4 0x02 /* I can handle TSO for IPv4 packets */ +#define TUN_F_TSO6 0x04 /* I can handle TSO for IPv6 packets */ +#define TUN_F_TSO_ECN 0x08 /* I can handle TSO with ECN bits. */ + struct tun_pi { unsigned short flags; __be16 proto; -- cgit v1.2.3 From f43798c27684ab925adde7d8acc34c78c6e50df8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 3 Jul 2008 03:48:02 -0700 Subject: tun: Allow GSO using virtio_net_hdr Add a IFF_VNET_HDR flag. This uses the same ABI as virtio_net (ie. prepending struct virtio_net_hdr to packets) to indicate GSO and checksum information. Signed-off-by: Rusty Russell Acked-by: Max Krasnyansky Signed-off-by: David S. Miller --- drivers/net/tun.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++-- include/linux/if_tun.h | 2 ++ 2 files changed, 94 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index a314955e6994..aa4ee4439f04 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -63,6 +63,7 @@ #include #include #include +#include #include #include @@ -283,6 +284,7 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, struct iovec *iv, struct tun_pi pi = { 0, __constant_htons(ETH_P_IP) }; struct sk_buff *skb; size_t len = count, align = 0; + struct virtio_net_hdr gso = { 0 }; if (!(tun->flags & TUN_NO_PI)) { if ((len -= sizeof(pi)) > count) @@ -292,6 +294,17 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, struct iovec *iv, return -EFAULT; } + if (tun->flags & TUN_VNET_HDR) { + if ((len -= sizeof(gso)) > count) + return -EINVAL; + + if (memcpy_fromiovec((void *)&gso, iv, sizeof(gso))) + return -EFAULT; + + if (gso.hdr_len > len) + return -EINVAL; + } + if ((tun->flags & TUN_TYPE_MASK) == TUN_TAP_DEV) { align = NET_IP_ALIGN; if (unlikely(len < ETH_HLEN)) @@ -311,6 +324,16 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, struct iovec *iv, return -EFAULT; } + if (gso.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { + if (!skb_partial_csum_set(skb, gso.csum_start, + gso.csum_offset)) { + tun->dev->stats.rx_frame_errors++; + kfree_skb(skb); + return -EINVAL; + } + } else if (tun->flags & TUN_NOCHECKSUM) + skb->ip_summed = CHECKSUM_UNNECESSARY; + switch (tun->flags & TUN_TYPE_MASK) { case TUN_TUN_DEV: if (tun->flags & TUN_NO_PI) { @@ -337,8 +360,35 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, struct iovec *iv, break; }; - if (tun->flags & TUN_NOCHECKSUM) - skb->ip_summed = CHECKSUM_UNNECESSARY; + if (gso.gso_type != VIRTIO_NET_HDR_GSO_NONE) { + pr_debug("GSO!\n"); + switch (gso.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { + case VIRTIO_NET_HDR_GSO_TCPV4: + skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; + break; + case VIRTIO_NET_HDR_GSO_TCPV6: + skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6; + break; + default: + tun->dev->stats.rx_frame_errors++; + kfree_skb(skb); + return -EINVAL; + } + + if (gso.gso_type & VIRTIO_NET_HDR_GSO_ECN) + skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN; + + skb_shinfo(skb)->gso_size = gso.gso_size; + if (skb_shinfo(skb)->gso_size == 0) { + tun->dev->stats.rx_frame_errors++; + kfree_skb(skb); + return -EINVAL; + } + + /* Header must be checked, and gso_segs computed. */ + skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY; + skb_shinfo(skb)->gso_segs = 0; + } netif_rx_ni(skb); tun->dev->last_rx = jiffies; @@ -384,6 +434,39 @@ static __inline__ ssize_t tun_put_user(struct tun_struct *tun, total += sizeof(pi); } + if (tun->flags & TUN_VNET_HDR) { + struct virtio_net_hdr gso = { 0 }; /* no info leak */ + if ((len -= sizeof(gso)) < 0) + return -EINVAL; + + if (skb_is_gso(skb)) { + struct skb_shared_info *sinfo = skb_shinfo(skb); + + /* This is a hint as to how much should be linear. */ + gso.hdr_len = skb_headlen(skb); + gso.gso_size = sinfo->gso_size; + if (sinfo->gso_type & SKB_GSO_TCPV4) + gso.gso_type = VIRTIO_NET_HDR_GSO_TCPV4; + else if (sinfo->gso_type & SKB_GSO_TCPV6) + gso.gso_type = VIRTIO_NET_HDR_GSO_TCPV6; + else + BUG(); + if (sinfo->gso_type & SKB_GSO_TCP_ECN) + gso.gso_type |= VIRTIO_NET_HDR_GSO_ECN; + } else + gso.gso_type = VIRTIO_NET_HDR_GSO_NONE; + + if (skb->ip_summed == CHECKSUM_PARTIAL) { + gso.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; + gso.csum_start = skb->csum_start - skb_headroom(skb); + gso.csum_offset = skb->csum_offset; + } /* else everything is zero */ + + if (unlikely(memcpy_toiovec(iv, (void *)&gso, sizeof(gso)))) + return -EFAULT; + total += sizeof(gso); + } + len = min_t(int, skb->len, len); skb_copy_datagram_iovec(skb, 0, iv, len); @@ -598,6 +681,11 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) else tun->flags &= ~TUN_ONE_QUEUE; + if (ifr->ifr_flags & IFF_VNET_HDR) + tun->flags |= TUN_VNET_HDR; + else + tun->flags &= ~TUN_VNET_HDR; + file->private_data = tun; tun->attached = 1; get_net(dev_net(tun->dev)); @@ -684,7 +772,8 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, /* Currently this just means: "what IFF flags are valid?". * This is needed because we never checked for invalid flags on * TUNSETIFF. */ - return put_user(IFF_TUN | IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE, + return put_user(IFF_TUN | IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE | + IFF_VNET_HDR, (unsigned int __user*)argp); } diff --git a/include/linux/if_tun.h b/include/linux/if_tun.h index 3f0a0995d449..563fae542da6 100644 --- a/include/linux/if_tun.h +++ b/include/linux/if_tun.h @@ -31,6 +31,7 @@ #define TUN_NO_PI 0x0040 #define TUN_ONE_QUEUE 0x0080 #define TUN_PERSIST 0x0100 +#define TUN_VNET_HDR 0x0200 /* Ioctl defines */ #define TUNSETNOCSUM _IOW('T', 200, int) @@ -48,6 +49,7 @@ #define IFF_TAP 0x0002 #define IFF_NO_PI 0x1000 #define IFF_ONE_QUEUE 0x2000 +#define IFF_VNET_HDR 0x4000 /* Features for GSO (TUNSETOFFLOAD). */ #define TUN_F_CSUM 0x01 /* You can hand me unchecksummed packets. */ -- cgit v1.2.3 From 198191c4a7ce4daba379608fb38b9bc5a4eedc61 Mon Sep 17 00:00:00 2001 From: Krzysztof Halasa Date: Mon, 30 Jun 2008 23:26:53 +0200 Subject: WAN: convert drivers to use built-in netdev_stats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no point in using separate net_device_stats structs when the one in struct net_device is present. Compiles. Signed-off-by: Krzysztof HaÅ‚asa Signed-off-by: Jeff Garzik --- drivers/char/pcmcia/synclink_cs.c | 28 +++++++-------- drivers/char/synclink.c | 33 +++++++++--------- drivers/char/synclink_gt.c | 28 +++++++-------- drivers/char/synclinkmp.c | 31 ++++++++--------- drivers/net/wan/c101.c | 6 ++-- drivers/net/wan/dscc4.c | 22 ++++++------ drivers/net/wan/farsync.c | 70 +++++++++++++++++--------------------- drivers/net/wan/hd6457x.c | 33 +++++++++--------- drivers/net/wan/hdlc.c | 2 +- drivers/net/wan/hdlc_cisco.c | 4 +-- drivers/net/wan/hdlc_fr.c | 29 ++++++---------- drivers/net/wan/hdlc_raw_eth.c | 2 +- drivers/net/wan/hdlc_x25.c | 6 ++-- drivers/net/wan/pc300_drv.c | 71 +++++++++++++++++---------------------- drivers/net/wan/pc300_tty.c | 6 ++-- drivers/net/wan/wanxl.c | 26 +++++++------- include/linux/hdlc.h | 7 ---- 17 files changed, 174 insertions(+), 230 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index 1dd0e992c83d..fb2fb159faa3 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -3886,9 +3886,8 @@ static bool rx_get_frame(MGSLPC_INFO *info) framesize = 0; #if SYNCLINK_GENERIC_HDLC { - struct net_device_stats *stats = hdlc_stats(info->netdev); - stats->rx_errors++; - stats->rx_frame_errors++; + info->netdev->stats.rx_errors++; + info->netdev->stats.rx_frame_errors++; } #endif } else @@ -4144,7 +4143,6 @@ static int hdlcdev_attach(struct net_device *dev, unsigned short encoding, static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) { MGSLPC_INFO *info = dev_to_port(dev); - struct net_device_stats *stats = hdlc_stats(dev); unsigned long flags; if (debug_level >= DEBUG_LEVEL_INFO) @@ -4159,8 +4157,8 @@ static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) info->tx_put = info->tx_count = skb->len; /* update network statistics */ - stats->tx_packets++; - stats->tx_bytes += skb->len; + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; /* done with socket buffer, so free it */ dev_kfree_skb(skb); @@ -4376,14 +4374,13 @@ static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) static void hdlcdev_tx_timeout(struct net_device *dev) { MGSLPC_INFO *info = dev_to_port(dev); - struct net_device_stats *stats = hdlc_stats(dev); unsigned long flags; if (debug_level >= DEBUG_LEVEL_INFO) printk("hdlcdev_tx_timeout(%s)\n",dev->name); - stats->tx_errors++; - stats->tx_aborted_errors++; + dev->stats.tx_errors++; + dev->stats.tx_aborted_errors++; spin_lock_irqsave(&info->lock,flags); tx_stop(info); @@ -4416,27 +4413,26 @@ static void hdlcdev_rx(MGSLPC_INFO *info, char *buf, int size) { struct sk_buff *skb = dev_alloc_skb(size); struct net_device *dev = info->netdev; - struct net_device_stats *stats = hdlc_stats(dev); if (debug_level >= DEBUG_LEVEL_INFO) printk("hdlcdev_rx(%s)\n",dev->name); if (skb == NULL) { printk(KERN_NOTICE "%s: can't alloc skb, dropping packet\n", dev->name); - stats->rx_dropped++; + dev->stats.rx_dropped++; return; } - memcpy(skb_put(skb, size),buf,size); + memcpy(skb_put(skb, size), buf, size); - skb->protocol = hdlc_type_trans(skb, info->netdev); + skb->protocol = hdlc_type_trans(skb, dev); - stats->rx_packets++; - stats->rx_bytes += size; + dev->stats.rx_packets++; + dev->stats.rx_bytes += size; netif_rx(skb); - info->netdev->last_rx = jiffies; + dev->last_rx = jiffies; } /** diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index ac5080df2565..9d247d8a87a3 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -6640,9 +6640,8 @@ static bool mgsl_get_rx_frame(struct mgsl_struct *info) framesize = 0; #if SYNCLINK_GENERIC_HDLC { - struct net_device_stats *stats = hdlc_stats(info->netdev); - stats->rx_errors++; - stats->rx_frame_errors++; + info->netdev->stats.rx_errors++; + info->netdev->stats.rx_frame_errors++; } #endif } else @@ -7753,7 +7752,6 @@ static int hdlcdev_attach(struct net_device *dev, unsigned short encoding, static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) { struct mgsl_struct *info = dev_to_port(dev); - struct net_device_stats *stats = hdlc_stats(dev); unsigned long flags; if (debug_level >= DEBUG_LEVEL_INFO) @@ -7767,8 +7765,8 @@ static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) mgsl_load_tx_dma_buffer(info, skb->data, skb->len); /* update network statistics */ - stats->tx_packets++; - stats->tx_bytes += skb->len; + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; /* done with socket buffer, so free it */ dev_kfree_skb(skb); @@ -7984,14 +7982,13 @@ static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) static void hdlcdev_tx_timeout(struct net_device *dev) { struct mgsl_struct *info = dev_to_port(dev); - struct net_device_stats *stats = hdlc_stats(dev); unsigned long flags; if (debug_level >= DEBUG_LEVEL_INFO) printk("hdlcdev_tx_timeout(%s)\n",dev->name); - stats->tx_errors++; - stats->tx_aborted_errors++; + dev->stats.tx_errors++; + dev->stats.tx_aborted_errors++; spin_lock_irqsave(&info->irq_spinlock,flags); usc_stop_transmitter(info); @@ -8024,27 +8021,27 @@ static void hdlcdev_rx(struct mgsl_struct *info, char *buf, int size) { struct sk_buff *skb = dev_alloc_skb(size); struct net_device *dev = info->netdev; - struct net_device_stats *stats = hdlc_stats(dev); if (debug_level >= DEBUG_LEVEL_INFO) - printk("hdlcdev_rx(%s)\n",dev->name); + printk("hdlcdev_rx(%s)\n", dev->name); if (skb == NULL) { - printk(KERN_NOTICE "%s: can't alloc skb, dropping packet\n", dev->name); - stats->rx_dropped++; + printk(KERN_NOTICE "%s: can't alloc skb, dropping packet\n", + dev->name); + dev->stats.rx_dropped++; return; } - memcpy(skb_put(skb, size),buf,size); + memcpy(skb_put(skb, size), buf, size); - skb->protocol = hdlc_type_trans(skb, info->netdev); + skb->protocol = hdlc_type_trans(skb, dev); - stats->rx_packets++; - stats->rx_bytes += size; + dev->stats.rx_packets++; + dev->stats.rx_bytes += size; netif_rx(skb); - info->netdev->last_rx = jiffies; + dev->last_rx = jiffies; } /** diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index 55c1653be00c..d88a607e34b7 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -1544,7 +1544,6 @@ static int hdlcdev_attach(struct net_device *dev, unsigned short encoding, static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) { struct slgt_info *info = dev_to_port(dev); - struct net_device_stats *stats = hdlc_stats(dev); unsigned long flags; DBGINFO(("%s hdlc_xmit\n", dev->name)); @@ -1557,8 +1556,8 @@ static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) tx_load(info, skb->data, skb->len); /* update network statistics */ - stats->tx_packets++; - stats->tx_bytes += skb->len; + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; /* done with socket buffer, so free it */ dev_kfree_skb(skb); @@ -1775,13 +1774,12 @@ static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) static void hdlcdev_tx_timeout(struct net_device *dev) { struct slgt_info *info = dev_to_port(dev); - struct net_device_stats *stats = hdlc_stats(dev); unsigned long flags; DBGINFO(("%s hdlcdev_tx_timeout\n", dev->name)); - stats->tx_errors++; - stats->tx_aborted_errors++; + dev->stats.tx_errors++; + dev->stats.tx_aborted_errors++; spin_lock_irqsave(&info->lock,flags); tx_stop(info); @@ -1814,26 +1812,25 @@ static void hdlcdev_rx(struct slgt_info *info, char *buf, int size) { struct sk_buff *skb = dev_alloc_skb(size); struct net_device *dev = info->netdev; - struct net_device_stats *stats = hdlc_stats(dev); DBGINFO(("%s hdlcdev_rx\n", dev->name)); if (skb == NULL) { DBGERR(("%s: can't alloc skb, drop packet\n", dev->name)); - stats->rx_dropped++; + dev->stats.rx_dropped++; return; } - memcpy(skb_put(skb, size),buf,size); + memcpy(skb_put(skb, size), buf, size); - skb->protocol = hdlc_type_trans(skb, info->netdev); + skb->protocol = hdlc_type_trans(skb, dev); - stats->rx_packets++; - stats->rx_bytes += size; + dev->stats.rx_packets++; + dev->stats.rx_bytes += size; netif_rx(skb); - info->netdev->last_rx = jiffies; + dev->last_rx = jiffies; } /** @@ -4577,9 +4574,8 @@ check_again: #if SYNCLINK_GENERIC_HDLC if (framesize == 0) { - struct net_device_stats *stats = hdlc_stats(info->netdev); - stats->rx_errors++; - stats->rx_frame_errors++; + info->netdev->stats.rx_errors++; + info->netdev->stats.rx_frame_errors++; } #endif diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c index bec54866e0bb..10241ed86100 100644 --- a/drivers/char/synclinkmp.c +++ b/drivers/char/synclinkmp.c @@ -1678,7 +1678,6 @@ static int hdlcdev_attach(struct net_device *dev, unsigned short encoding, static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) { SLMP_INFO *info = dev_to_port(dev); - struct net_device_stats *stats = hdlc_stats(dev); unsigned long flags; if (debug_level >= DEBUG_LEVEL_INFO) @@ -1692,8 +1691,8 @@ static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) tx_load_dma_buffer(info, skb->data, skb->len); /* update network statistics */ - stats->tx_packets++; - stats->tx_bytes += skb->len; + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; /* done with socket buffer, so free it */ dev_kfree_skb(skb); @@ -1909,14 +1908,13 @@ static int hdlcdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) static void hdlcdev_tx_timeout(struct net_device *dev) { SLMP_INFO *info = dev_to_port(dev); - struct net_device_stats *stats = hdlc_stats(dev); unsigned long flags; if (debug_level >= DEBUG_LEVEL_INFO) printk("hdlcdev_tx_timeout(%s)\n",dev->name); - stats->tx_errors++; - stats->tx_aborted_errors++; + dev->stats.tx_errors++; + dev->stats.tx_aborted_errors++; spin_lock_irqsave(&info->lock,flags); tx_stop(info); @@ -1949,27 +1947,27 @@ static void hdlcdev_rx(SLMP_INFO *info, char *buf, int size) { struct sk_buff *skb = dev_alloc_skb(size); struct net_device *dev = info->netdev; - struct net_device_stats *stats = hdlc_stats(dev); if (debug_level >= DEBUG_LEVEL_INFO) printk("hdlcdev_rx(%s)\n",dev->name); if (skb == NULL) { - printk(KERN_NOTICE "%s: can't alloc skb, dropping packet\n", dev->name); - stats->rx_dropped++; + printk(KERN_NOTICE "%s: can't alloc skb, dropping packet\n", + dev->name); + dev->stats.rx_dropped++; return; } - memcpy(skb_put(skb, size),buf,size); + memcpy(skb_put(skb, size), buf, size); - skb->protocol = hdlc_type_trans(skb, info->netdev); + skb->protocol = hdlc_type_trans(skb, dev); - stats->rx_packets++; - stats->rx_bytes += size; + dev->stats.rx_packets++; + dev->stats.rx_bytes += size; netif_rx(skb); - info->netdev->last_rx = jiffies; + dev->last_rx = jiffies; } /** @@ -4983,9 +4981,8 @@ CheckAgain: framesize = 0; #if SYNCLINK_GENERIC_HDLC { - struct net_device_stats *stats = hdlc_stats(info->netdev); - stats->rx_errors++; - stats->rx_frame_errors++; + info->netdev->stats.rx_errors++; + info->netdev->stats.rx_frame_errors++; } #endif } diff --git a/drivers/net/wan/c101.c b/drivers/net/wan/c101.c index c2cc42f723d5..c8e563106a4a 100644 --- a/drivers/net/wan/c101.c +++ b/drivers/net/wan/c101.c @@ -133,9 +133,9 @@ static void sca_msci_intr(port_t *port) sca_out(stat & (ST1_UDRN | ST1_CDCD), MSCI0_OFFSET + ST1, port); if (stat & ST1_UDRN) { - struct net_device_stats *stats = hdlc_stats(port_to_dev(port)); - stats->tx_errors++; /* TX Underrun error detected */ - stats->tx_fifo_errors++; + /* TX Underrun error detected */ + port_to_dev(port)->stats.tx_errors++; + port_to_dev(port)->stats.tx_fifo_errors++; } stat = sca_in(MSCI1_OFFSET + ST1, port); /* read MSCI1 ST1 status */ diff --git a/drivers/net/wan/dscc4.c b/drivers/net/wan/dscc4.c index c6f26e28e376..50ef5b4efd6d 100644 --- a/drivers/net/wan/dscc4.c +++ b/drivers/net/wan/dscc4.c @@ -642,7 +642,6 @@ static inline void dscc4_rx_skb(struct dscc4_dev_priv *dpriv, struct net_device *dev) { struct RxFD *rx_fd = dpriv->rx_fd + dpriv->rx_current%RX_RING_SIZE; - struct net_device_stats *stats = hdlc_stats(dev); struct pci_dev *pdev = dpriv->pci_priv->pdev; struct sk_buff *skb; int pkt_len; @@ -656,8 +655,8 @@ static inline void dscc4_rx_skb(struct dscc4_dev_priv *dpriv, pci_unmap_single(pdev, le32_to_cpu(rx_fd->data), RX_MAX(HDLC_MAX_MRU), PCI_DMA_FROMDEVICE); if ((skb->data[--pkt_len] & FrameOk) == FrameOk) { - stats->rx_packets++; - stats->rx_bytes += pkt_len; + dev->stats.rx_packets++; + dev->stats.rx_bytes += pkt_len; skb_put(skb, pkt_len); if (netif_running(dev)) skb->protocol = hdlc_type_trans(skb, dev); @@ -665,13 +664,13 @@ static inline void dscc4_rx_skb(struct dscc4_dev_priv *dpriv, netif_rx(skb); } else { if (skb->data[pkt_len] & FrameRdo) - stats->rx_fifo_errors++; + dev->stats.rx_fifo_errors++; else if (!(skb->data[pkt_len] | ~FrameCrc)) - stats->rx_crc_errors++; + dev->stats.rx_crc_errors++; else if (!(skb->data[pkt_len] | ~(FrameVfr | FrameRab))) - stats->rx_length_errors++; + dev->stats.rx_length_errors++; else - stats->rx_errors++; + dev->stats.rx_errors++; dev_kfree_skb_irq(skb); } refill: @@ -1569,7 +1568,6 @@ try: if (state & SccEvt) { if (state & Alls) { - struct net_device_stats *stats = hdlc_stats(dev); struct sk_buff *skb; struct TxFD *tx_fd; @@ -1586,8 +1584,8 @@ try: pci_unmap_single(ppriv->pdev, le32_to_cpu(tx_fd->data), skb->len, PCI_DMA_TODEVICE); if (tx_fd->state & FrameEnd) { - stats->tx_packets++; - stats->tx_bytes += skb->len; + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; } dev_kfree_skb_irq(skb); dpriv->tx_skbuff[cur] = NULL; @@ -1698,7 +1696,7 @@ try: } if (state & Err) { printk(KERN_INFO "%s: Tx ERR\n", dev->name); - hdlc_stats(dev)->tx_errors++; + dev->stats.tx_errors++; state &= ~Err; } } @@ -1834,7 +1832,7 @@ try: if (!(rx_fd->state2 & DataComplete)) break; if (rx_fd->state2 & FrameAborted) { - hdlc_stats(dev)->rx_over_errors++; + dev->stats.rx_over_errors++; rx_fd->state1 |= Hold; rx_fd->state2 = 0x00000000; rx_fd->end = cpu_to_le32(0xbabeface); diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c index 547368e9633d..754f00809e3e 100644 --- a/drivers/net/wan/farsync.c +++ b/drivers/net/wan/farsync.c @@ -845,7 +845,6 @@ fst_tx_dma_complete(struct fst_card_info *card, struct fst_port_info *port, int len, int txpos) { struct net_device *dev = port_to_dev(port); - struct net_device_stats *stats = hdlc_stats(dev); /* * Everything is now set, just tell the card to go @@ -853,8 +852,8 @@ fst_tx_dma_complete(struct fst_card_info *card, struct fst_port_info *port, dbg(DBG_TX, "fst_tx_dma_complete\n"); FST_WRB(card, txDescrRing[port->index][txpos].bits, DMA_OWN | TX_STP | TX_ENP); - stats->tx_packets++; - stats->tx_bytes += len; + dev->stats.tx_packets++; + dev->stats.tx_bytes += len; dev->trans_start = jiffies; } @@ -876,7 +875,6 @@ fst_rx_dma_complete(struct fst_card_info *card, struct fst_port_info *port, int len, struct sk_buff *skb, int rxp) { struct net_device *dev = port_to_dev(port); - struct net_device_stats *stats = hdlc_stats(dev); int pi; int rx_status; @@ -888,8 +886,8 @@ fst_rx_dma_complete(struct fst_card_info *card, struct fst_port_info *port, FST_WRB(card, rxDescrRing[pi][rxp].bits, DMA_OWN); /* Update stats */ - stats->rx_packets++; - stats->rx_bytes += len; + dev->stats.rx_packets++; + dev->stats.rx_bytes += len; /* Push upstream */ dbg(DBG_RX, "Pushing the frame up the stack\n"); @@ -900,7 +898,7 @@ fst_rx_dma_complete(struct fst_card_info *card, struct fst_port_info *port, rx_status = netif_rx(skb); fst_process_rx_status(rx_status, port_to_dev(port)->name); if (rx_status == NET_RX_DROP) - stats->rx_dropped++; + dev->stats.rx_dropped++; dev->last_rx = jiffies; } @@ -1163,29 +1161,28 @@ fst_log_rx_error(struct fst_card_info *card, struct fst_port_info *port, unsigned char dmabits, int rxp, unsigned short len) { struct net_device *dev = port_to_dev(port); - struct net_device_stats *stats = hdlc_stats(dev); - /* + /* * Increment the appropriate error counter */ - stats->rx_errors++; + dev->stats.rx_errors++; if (dmabits & RX_OFLO) { - stats->rx_fifo_errors++; + dev->stats.rx_fifo_errors++; dbg(DBG_ASS, "Rx fifo error on card %d port %d buffer %d\n", card->card_no, port->index, rxp); } if (dmabits & RX_CRC) { - stats->rx_crc_errors++; + dev->stats.rx_crc_errors++; dbg(DBG_ASS, "Rx crc error on card %d port %d\n", card->card_no, port->index); } if (dmabits & RX_FRAM) { - stats->rx_frame_errors++; + dev->stats.rx_frame_errors++; dbg(DBG_ASS, "Rx frame error on card %d port %d\n", card->card_no, port->index); } if (dmabits == (RX_STP | RX_ENP)) { - stats->rx_length_errors++; + dev->stats.rx_length_errors++; dbg(DBG_ASS, "Rx length error (%d) on card %d port %d\n", len, card->card_no, port->index); } @@ -1242,7 +1239,6 @@ fst_intr_rx(struct fst_card_info *card, struct fst_port_info *port) unsigned short len; struct sk_buff *skb; struct net_device *dev = port_to_dev(port); - struct net_device_stats *stats = hdlc_stats(dev); /* Check we have a buffer to process */ pi = port->index; @@ -1291,7 +1287,7 @@ fst_intr_rx(struct fst_card_info *card, struct fst_port_info *port) if ((skb = dev_alloc_skb(len)) == NULL) { dbg(DBG_RX, "intr_rx: can't allocate buffer\n"); - stats->rx_dropped++; + dev->stats.rx_dropped++; /* Return descriptor to card */ FST_WRB(card, rxDescrRing[pi][rxp].bits, DMA_OWN); @@ -1316,8 +1312,8 @@ fst_intr_rx(struct fst_card_info *card, struct fst_port_info *port) FST_WRB(card, rxDescrRing[pi][rxp].bits, DMA_OWN); /* Update stats */ - stats->rx_packets++; - stats->rx_bytes += len; + dev->stats.rx_packets++; + dev->stats.rx_bytes += len; /* Push upstream */ dbg(DBG_RX, "Pushing frame up the stack\n"); @@ -1327,9 +1323,8 @@ fst_intr_rx(struct fst_card_info *card, struct fst_port_info *port) skb->protocol = hdlc_type_trans(skb, dev); rx_status = netif_rx(skb); fst_process_rx_status(rx_status, port_to_dev(port)->name); - if (rx_status == NET_RX_DROP) { - stats->rx_dropped++; - } + if (rx_status == NET_RX_DROP) + dev->stats.rx_dropped++; dev->last_rx = jiffies; } else { card->dma_skb_rx = skb; @@ -1361,7 +1356,6 @@ do_bottom_half_tx(struct fst_card_info *card) struct sk_buff *skb; unsigned long flags; struct net_device *dev; - struct net_device_stats *stats; /* * Find a free buffer for the transmit @@ -1373,12 +1367,10 @@ do_bottom_half_tx(struct fst_card_info *card) if (!port->run) continue; - dev = port_to_dev(port); - stats = hdlc_stats(dev); - while (! - (FST_RDB(card, txDescrRing[pi][port->txpos].bits) & - DMA_OWN) - && !(card->dmatx_in_progress)) { + dev = port_to_dev(port); + while (!(FST_RDB(card, txDescrRing[pi][port->txpos].bits) & + DMA_OWN) + && !(card->dmatx_in_progress)) { /* * There doesn't seem to be a txdone event per-se * We seem to have to deduce it, by checking the DMA_OWN @@ -1422,8 +1414,8 @@ do_bottom_half_tx(struct fst_card_info *card) txDescrRing[pi][port->txpos]. bits, DMA_OWN | TX_STP | TX_ENP); - stats->tx_packets++; - stats->tx_bytes += skb->len; + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; dev->trans_start = jiffies; } else { /* Or do it through dma */ @@ -1628,8 +1620,8 @@ fst_intr(int dummy, void *dev_id) * always load up the entire packet for DMA. */ dbg(DBG_TX, "Tx underflow port %d\n", port->index); - hdlc_stats(port_to_dev(port))->tx_errors++; - hdlc_stats(port_to_dev(port))->tx_fifo_errors++; + port_to_dev(port)->stats.tx_errors++; + port_to_dev(port)->stats.tx_fifo_errors++; dbg(DBG_ASS, "Tx underflow on card %d port %d\n", card->card_no, port->index); break; @@ -2292,12 +2284,11 @@ fst_tx_timeout(struct net_device *dev) { struct fst_port_info *port; struct fst_card_info *card; - struct net_device_stats *stats = hdlc_stats(dev); port = dev_to_port(dev); card = port->card; - stats->tx_errors++; - stats->tx_aborted_errors++; + dev->stats.tx_errors++; + dev->stats.tx_aborted_errors++; dbg(DBG_ASS, "Tx timeout card %d port %d\n", card->card_no, port->index); fst_issue_cmd(port, ABORTTX); @@ -2312,7 +2303,6 @@ fst_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct fst_card_info *card; struct fst_port_info *port; - struct net_device_stats *stats = hdlc_stats(dev); unsigned long flags; int txq_length; @@ -2323,8 +2313,8 @@ fst_start_xmit(struct sk_buff *skb, struct net_device *dev) /* Drop packet with error if we don't have carrier */ if (!netif_carrier_ok(dev)) { dev_kfree_skb(skb); - stats->tx_errors++; - stats->tx_carrier_errors++; + dev->stats.tx_errors++; + dev->stats.tx_carrier_errors++; dbg(DBG_ASS, "Tried to transmit but no carrier on card %d port %d\n", card->card_no, port->index); @@ -2336,7 +2326,7 @@ fst_start_xmit(struct sk_buff *skb, struct net_device *dev) dbg(DBG_ASS, "Packet too large %d vs %d\n", skb->len, LEN_TX_BUFFER); dev_kfree_skb(skb); - stats->tx_errors++; + dev->stats.tx_errors++; return 0; } @@ -2368,7 +2358,7 @@ fst_start_xmit(struct sk_buff *skb, struct net_device *dev) * This shouldn't have happened but such is life */ dev_kfree_skb(skb); - stats->tx_errors++; + dev->stats.tx_errors++; dbg(DBG_ASS, "Tx queue overflow card %d port %d\n", card->card_no, port->index); return 0; diff --git a/drivers/net/wan/hd6457x.c b/drivers/net/wan/hd6457x.c index 8d0a1f2f00e5..591fb45a7c68 100644 --- a/drivers/net/wan/hd6457x.c +++ b/drivers/net/wan/hd6457x.c @@ -271,9 +271,9 @@ static inline void sca_msci_intr(port_t *port) sca_out(stat & (ST1_UDRN | ST1_CDCD), msci + ST1, card); if (stat & ST1_UDRN) { - struct net_device_stats *stats = hdlc_stats(port_to_dev(port)); - stats->tx_errors++; /* TX Underrun error detected */ - stats->tx_fifo_errors++; + /* TX Underrun error detected */ + port_to_dev(port)->stats.tx_errors++; + port_to_dev(port)->stats.tx_fifo_errors++; } if (stat & ST1_CDCD) @@ -286,7 +286,6 @@ static inline void sca_msci_intr(port_t *port) static inline void sca_rx(card_t *card, port_t *port, pkt_desc __iomem *desc, u16 rxin) { struct net_device *dev = port_to_dev(port); - struct net_device_stats *stats = hdlc_stats(dev); struct sk_buff *skb; u16 len; u32 buff; @@ -298,7 +297,7 @@ static inline void sca_rx(card_t *card, port_t *port, pkt_desc __iomem *desc, u1 len = readw(&desc->len); skb = dev_alloc_skb(len); if (!skb) { - stats->rx_dropped++; + dev->stats.rx_dropped++; return; } @@ -327,8 +326,8 @@ static inline void sca_rx(card_t *card, port_t *port, pkt_desc __iomem *desc, u1 printk(KERN_DEBUG "%s RX(%i):", dev->name, skb->len); debug_frame(skb); #endif - stats->rx_packets++; - stats->rx_bytes += skb->len; + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; dev->last_rx = jiffies; skb->protocol = hdlc_type_trans(skb, dev); netif_rx(skb); @@ -339,17 +338,18 @@ static inline void sca_rx(card_t *card, port_t *port, pkt_desc __iomem *desc, u1 /* Receive DMA interrupt service */ static inline void sca_rx_intr(port_t *port) { + struct net_device *dev = port_to_dev(port); u16 dmac = get_dmac_rx(port); card_t *card = port_to_card(port); u8 stat = sca_in(DSR_RX(phy_node(port)), card); /* read DMA Status */ - struct net_device_stats *stats = hdlc_stats(port_to_dev(port)); /* Reset DSR status bits */ sca_out((stat & (DSR_EOT | DSR_EOM | DSR_BOF | DSR_COF)) | DSR_DWE, DSR_RX(phy_node(port)), card); if (stat & DSR_BOF) - stats->rx_over_errors++; /* Dropped one or more frames */ + /* Dropped one or more frames */ + dev->stats.rx_over_errors++; while (1) { u32 desc_off = desc_offset(port, port->rxin, 0); @@ -364,12 +364,14 @@ static inline void sca_rx_intr(port_t *port) if (!(stat & ST_RX_EOM)) port->rxpart = 1; /* partial frame received */ else if ((stat & ST_ERROR_MASK) || port->rxpart) { - stats->rx_errors++; - if (stat & ST_RX_OVERRUN) stats->rx_fifo_errors++; + dev->stats.rx_errors++; + if (stat & ST_RX_OVERRUN) + dev->stats.rx_fifo_errors++; else if ((stat & (ST_RX_SHORT | ST_RX_ABORT | ST_RX_RESBIT)) || port->rxpart) - stats->rx_frame_errors++; - else if (stat & ST_RX_CRC) stats->rx_crc_errors++; + dev->stats.rx_frame_errors++; + else if (stat & ST_RX_CRC) + dev->stats.rx_crc_errors++; if (stat & ST_RX_EOM) port->rxpart = 0; /* received last fragment */ } else @@ -390,7 +392,6 @@ static inline void sca_rx_intr(port_t *port) static inline void sca_tx_intr(port_t *port) { struct net_device *dev = port_to_dev(port); - struct net_device_stats *stats = hdlc_stats(dev); u16 dmac = get_dmac_tx(port); card_t* card = port_to_card(port); u8 stat; @@ -412,8 +413,8 @@ static inline void sca_tx_intr(port_t *port) break; /* Transmitter is/will_be sending this frame */ desc = desc_address(port, port->txlast, 1); - stats->tx_packets++; - stats->tx_bytes += readw(&desc->len); + dev->stats.tx_packets++; + dev->stats.tx_bytes += readw(&desc->len); writeb(0, &desc->stat); /* Free descriptor */ port->txlast = next_desc(port, port->txlast, 1); } diff --git a/drivers/net/wan/hdlc.c b/drivers/net/wan/hdlc.c index 7f984895b0d5..e3a536477c7e 100644 --- a/drivers/net/wan/hdlc.c +++ b/drivers/net/wan/hdlc.c @@ -57,7 +57,7 @@ static int hdlc_change_mtu(struct net_device *dev, int new_mtu) static struct net_device_stats *hdlc_get_stats(struct net_device *dev) { - return hdlc_stats(dev); + return &dev->stats; } diff --git a/drivers/net/wan/hdlc_cisco.c b/drivers/net/wan/hdlc_cisco.c index 762d21c1c703..849819c2552d 100644 --- a/drivers/net/wan/hdlc_cisco.c +++ b/drivers/net/wan/hdlc_cisco.c @@ -252,8 +252,8 @@ static int cisco_rx(struct sk_buff *skb) dev_kfree_skb_any(skb); return NET_RX_DROP; - rx_error: - dev_to_hdlc(dev)->stats.rx_errors++; /* Mark error */ +rx_error: + dev->stats.rx_errors++; /* Mark error */ dev_kfree_skb_any(skb); return NET_RX_DROP; } diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c index 520bb0b1a9a2..cb1b415053eb 100644 --- a/drivers/net/wan/hdlc_fr.c +++ b/drivers/net/wan/hdlc_fr.c @@ -136,7 +136,6 @@ typedef struct pvc_device_struct { }pvc_device; struct pvc_desc { - struct net_device_stats stats; pvc_device *pvc; }; @@ -184,11 +183,6 @@ static inline struct pvc_desc* pvcdev_to_desc(struct net_device *dev) return dev->priv; } -static inline struct net_device_stats* pvc_get_stats(struct net_device *dev) -{ - return &pvcdev_to_desc(dev)->stats; -} - static inline pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci) { pvc_device *pvc = state(hdlc)->first_pvc; @@ -425,7 +419,6 @@ static int pvc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) static int pvc_xmit(struct sk_buff *skb, struct net_device *dev) { pvc_device *pvc = pvcdev_to_desc(dev)->pvc; - struct net_device_stats *stats = pvc_get_stats(dev); if (pvc->state.active) { if (dev->type == ARPHRD_ETHER) { @@ -435,7 +428,7 @@ static int pvc_xmit(struct sk_buff *skb, struct net_device *dev) if (skb_tailroom(skb) < pad) if (pskb_expand_head(skb, 0, pad, GFP_ATOMIC)) { - stats->tx_dropped++; + dev->stats.tx_dropped++; dev_kfree_skb(skb); return 0; } @@ -445,17 +438,17 @@ static int pvc_xmit(struct sk_buff *skb, struct net_device *dev) skb->protocol = __constant_htons(ETH_P_802_3); } if (!fr_hard_header(&skb, pvc->dlci)) { - stats->tx_bytes += skb->len; - stats->tx_packets++; + dev->stats.tx_bytes += skb->len; + dev->stats.tx_packets++; if (pvc->state.fecn) /* TX Congestion counter */ - stats->tx_compressed++; + dev->stats.tx_compressed++; skb->dev = pvc->frad; dev_queue_xmit(skb); return 0; } } - stats->tx_dropped++; + dev->stats.tx_dropped++; dev_kfree_skb(skb); return 0; } @@ -955,7 +948,7 @@ static int fr_rx(struct sk_buff *skb) if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) { - dev_to_hdlc(frad)->stats.rx_dropped++; + frad->stats.rx_dropped++; return NET_RX_DROP; } @@ -1003,11 +996,10 @@ static int fr_rx(struct sk_buff *skb) } if (dev) { - struct net_device_stats *stats = pvc_get_stats(dev); - stats->rx_packets++; /* PVC traffic */ - stats->rx_bytes += skb->len; + dev->stats.rx_packets++; /* PVC traffic */ + dev->stats.rx_bytes += skb->len; if (pvc->state.becn) - stats->rx_compressed++; + dev->stats.rx_compressed++; netif_rx(skb); return NET_RX_SUCCESS; } else { @@ -1016,7 +1008,7 @@ static int fr_rx(struct sk_buff *skb) } rx_error: - dev_to_hdlc(frad)->stats.rx_errors++; /* Mark error */ + frad->stats.rx_errors++; /* Mark error */ dev_kfree_skb_any(skb); return NET_RX_DROP; } @@ -1122,7 +1114,6 @@ static int fr_add_pvc(struct net_device *frad, unsigned int dlci, int type) dlci_to_q922(dev->broadcast, dlci); } dev->hard_start_xmit = pvc_xmit; - dev->get_stats = pvc_get_stats; dev->open = pvc_open; dev->stop = pvc_close; dev->do_ioctl = pvc_ioctl; diff --git a/drivers/net/wan/hdlc_raw_eth.c b/drivers/net/wan/hdlc_raw_eth.c index d20c685f6711..26dee600506f 100644 --- a/drivers/net/wan/hdlc_raw_eth.c +++ b/drivers/net/wan/hdlc_raw_eth.c @@ -33,7 +33,7 @@ static int eth_tx(struct sk_buff *skb, struct net_device *dev) int len = skb->len; if (skb_tailroom(skb) < pad) if (pskb_expand_head(skb, 0, pad, GFP_ATOMIC)) { - hdlc_stats(dev)->tx_dropped++; + dev->stats.tx_dropped++; dev_kfree_skb(skb); return 0; } diff --git a/drivers/net/wan/hdlc_x25.c b/drivers/net/wan/hdlc_x25.c index c15cc11e399b..e808720030ef 100644 --- a/drivers/net/wan/hdlc_x25.c +++ b/drivers/net/wan/hdlc_x25.c @@ -164,17 +164,15 @@ static void x25_close(struct net_device *dev) static int x25_rx(struct sk_buff *skb) { - struct hdlc_device *hdlc = dev_to_hdlc(skb->dev); - if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) { - hdlc->stats.rx_dropped++; + skb->dev->stats.rx_dropped++; return NET_RX_DROP; } if (lapb_data_received(skb->dev, skb) == LAPB_OK) return NET_RX_SUCCESS; - hdlc->stats.rx_errors++; + skb->dev->stats.rx_errors++; dev_kfree_skb_any(skb); return NET_RX_DROP; } diff --git a/drivers/net/wan/pc300_drv.c b/drivers/net/wan/pc300_drv.c index 57914fbd41d3..334170527755 100644 --- a/drivers/net/wan/pc300_drv.c +++ b/drivers/net/wan/pc300_drv.c @@ -285,7 +285,6 @@ static void rx_dma_buf_init(pc300_t *, int); static void tx_dma_buf_check(pc300_t *, int); static void rx_dma_buf_check(pc300_t *, int); static irqreturn_t cpc_intr(int, void *); -static struct net_device_stats *cpc_get_stats(struct net_device *); static int clock_rate_calc(uclong, uclong, int *); static uclong detect_ram(pc300_t *); static void plx_init(pc300_t *); @@ -1775,13 +1774,12 @@ static void cpc_tx_timeout(struct net_device *dev) pc300dev_t *d = (pc300dev_t *) dev->priv; pc300ch_t *chan = (pc300ch_t *) d->chan; pc300_t *card = (pc300_t *) chan->card; - struct net_device_stats *stats = hdlc_stats(dev); int ch = chan->channel; unsigned long flags; ucchar ilar; - stats->tx_errors++; - stats->tx_aborted_errors++; + dev->stats.tx_errors++; + dev->stats.tx_aborted_errors++; CPC_LOCK(card, flags); if ((ilar = cpc_readb(card->hw.scabase + ILAR)) != 0) { printk("%s: ILAR=0x%x\n", dev->name, ilar); @@ -1803,7 +1801,6 @@ static int cpc_queue_xmit(struct sk_buff *skb, struct net_device *dev) pc300dev_t *d = (pc300dev_t *) dev->priv; pc300ch_t *chan = (pc300ch_t *) d->chan; pc300_t *card = (pc300_t *) chan->card; - struct net_device_stats *stats = hdlc_stats(dev); int ch = chan->channel; unsigned long flags; #ifdef PC300_DEBUG_TX @@ -1817,13 +1814,13 @@ static int cpc_queue_xmit(struct sk_buff *skb, struct net_device *dev) } else if (!netif_carrier_ok(dev)) { /* DCD must be OFF: drop packet */ dev_kfree_skb(skb); - stats->tx_errors++; - stats->tx_carrier_errors++; + dev->stats.tx_errors++; + dev->stats.tx_carrier_errors++; return 0; } else if (cpc_readb(card->hw.scabase + M_REG(ST3, ch)) & ST3_DCD) { printk("%s: DCD is OFF. Going administrative down.\n", dev->name); - stats->tx_errors++; - stats->tx_carrier_errors++; + dev->stats.tx_errors++; + dev->stats.tx_carrier_errors++; dev_kfree_skb(skb); netif_carrier_off(dev); CPC_LOCK(card, flags); @@ -1843,8 +1840,8 @@ static int cpc_queue_xmit(struct sk_buff *skb, struct net_device *dev) // printk("%s: write error. Dropping TX packet.\n", dev->name); netif_stop_queue(dev); dev_kfree_skb(skb); - stats->tx_errors++; - stats->tx_dropped++; + dev->stats.tx_errors++; + dev->stats.tx_dropped++; return 0; } #ifdef PC300_DEBUG_TX @@ -1886,7 +1883,6 @@ static void cpc_net_rx(struct net_device *dev) pc300dev_t *d = (pc300dev_t *) dev->priv; pc300ch_t *chan = (pc300ch_t *) d->chan; pc300_t *card = (pc300_t *) chan->card; - struct net_device_stats *stats = hdlc_stats(dev); int ch = chan->channel; #ifdef PC300_DEBUG_RX int i; @@ -1922,24 +1918,24 @@ static void cpc_net_rx(struct net_device *dev) #endif if ((skb == NULL) && (rxb > 0)) { /* rxb > dev->mtu */ - stats->rx_errors++; - stats->rx_length_errors++; + dev->stats.rx_errors++; + dev->stats.rx_length_errors++; continue; } if (rxb < 0) { /* Invalid frame */ rxb = -rxb; if (rxb & DST_OVR) { - stats->rx_errors++; - stats->rx_fifo_errors++; + dev->stats.rx_errors++; + dev->stats.rx_fifo_errors++; } if (rxb & DST_CRC) { - stats->rx_errors++; - stats->rx_crc_errors++; + dev->stats.rx_errors++; + dev->stats.rx_crc_errors++; } if (rxb & (DST_RBIT | DST_SHRT | DST_ABT)) { - stats->rx_errors++; - stats->rx_frame_errors++; + dev->stats.rx_errors++; + dev->stats.rx_frame_errors++; } } if (skb) { @@ -1948,7 +1944,7 @@ static void cpc_net_rx(struct net_device *dev) continue; } - stats->rx_bytes += rxb; + dev->stats.rx_bytes += rxb; #ifdef PC300_DEBUG_RX printk("%s R:", dev->name); @@ -1959,7 +1955,7 @@ static void cpc_net_rx(struct net_device *dev) if (d->trace_on) { cpc_trace(dev, skb, 'R'); } - stats->rx_packets++; + dev->stats.rx_packets++; skb->protocol = hdlc_type_trans(skb, dev); netif_rx(skb); } @@ -1974,16 +1970,15 @@ static void sca_tx_intr(pc300dev_t *dev) pc300_t *card = (pc300_t *)chan->card; int ch = chan->channel; volatile pcsca_bd_t __iomem * ptdescr; - struct net_device_stats *stats = hdlc_stats(dev->dev); /* Clean up descriptors from previous transmission */ ptdescr = (card->hw.rambase + TX_BD_ADDR(ch,chan->tx_first_bd)); - while ((cpc_readl(card->hw.scabase + DTX_REG(CDAL,ch)) != - TX_BD_ADDR(ch,chan->tx_first_bd)) && - (cpc_readb(&ptdescr->status) & DST_OSB)) { - stats->tx_packets++; - stats->tx_bytes += cpc_readw(&ptdescr->len); + while ((cpc_readl(card->hw.scabase + DTX_REG(CDAL,ch)) != + TX_BD_ADDR(ch,chan->tx_first_bd)) && + (cpc_readb(&ptdescr->status) & DST_OSB)) { + dev->dev->stats.tx_packets++; + dev->dev->stats.tx_bytes += cpc_readw(&ptdescr->len); cpc_writeb(&ptdescr->status, DST_OSB); cpc_writew(&ptdescr->len, 0); chan->nfree_tx_bd++; @@ -2048,8 +2043,8 @@ static void sca_intr(pc300_t * card) } cpc_net_rx(dev); /* Discard invalid frames */ - hdlc_stats(dev)->rx_errors++; - hdlc_stats(dev)->rx_over_errors++; + dev->stats.rx_errors++; + dev->stats.rx_over_errors++; chan->rx_first_bd = 0; chan->rx_last_bd = N_DMA_RX_BUF - 1; rx_dma_start(card, ch); @@ -2115,8 +2110,8 @@ static void sca_intr(pc300_t * card) card->hw.cpld_reg2) & ~ (CPLD_REG2_FALC_LED1 << (2 * ch))); } - hdlc_stats(dev)->tx_errors++; - hdlc_stats(dev)->tx_fifo_errors++; + dev->stats.tx_errors++; + dev->stats.tx_fifo_errors++; sca_tx_intr(d); } } @@ -2604,7 +2599,7 @@ static int cpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) case SIOCGPC300UTILSTATS: { if (!arg) { /* clear statistics */ - memset(hdlc_stats(dev), 0, sizeof(struct net_device_stats)); + memset(&dev->stats, 0, sizeof(dev->stats)); if (card->hw.type == PC300_TE) { memset(&chan->falc, 0, sizeof(falc_t)); } @@ -2615,8 +2610,8 @@ static int cpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) pc300stats.hw_type = card->hw.type; pc300stats.line_on = card->chan[ch].d.line_on; pc300stats.line_off = card->chan[ch].d.line_off; - memcpy(&pc300stats.gen_stats, hdlc_stats(dev), - sizeof(struct net_device_stats)); + memcpy(&pc300stats.gen_stats, &dev->stats, + sizeof(dev->stats)); if (card->hw.type == PC300_TE) memcpy(&pc300stats.te_stats,&chan->falc,sizeof(falc_t)); if (copy_to_user(arg, &pc300stats, sizeof(pc300stats_t))) @@ -2823,11 +2818,6 @@ static int cpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) } } -static struct net_device_stats *cpc_get_stats(struct net_device *dev) -{ - return hdlc_stats(dev); -} - static int clock_rate_calc(uclong rate, uclong clock, int *br_io) { int br, tc; @@ -3394,7 +3384,6 @@ static void cpc_init_card(pc300_t * card) dev->stop = cpc_close; dev->tx_timeout = cpc_tx_timeout; dev->watchdog_timeo = PC300_TX_TIMEOUT; - dev->get_stats = cpc_get_stats; dev->set_multicast_list = NULL; dev->set_mac_address = NULL; dev->change_mtu = cpc_change_mtu; diff --git a/drivers/net/wan/pc300_tty.c b/drivers/net/wan/pc300_tty.c index e03eef2f2282..eae94ab6b818 100644 --- a/drivers/net/wan/pc300_tty.c +++ b/drivers/net/wan/pc300_tty.c @@ -458,7 +458,7 @@ static int cpc_tty_write(struct tty_struct *tty, const unsigned char *buf, int c CPC_TTY_DBG("%s: cpc_tty_write data len=%i\n",cpc_tty->name,count); pc300chan = (pc300ch_t *)((pc300dev_t*)cpc_tty->pc300dev)->chan; - stats = hdlc_stats(((pc300dev_t*)cpc_tty->pc300dev)->dev); + stats = &cpc_tty->pc300dev->dev->stats; card = (pc300_t *) pc300chan->card; ch = pc300chan->channel; @@ -743,7 +743,7 @@ void cpc_tty_receive(pc300dev_t *pc300dev) pc300_t *card = (pc300_t *)pc300chan->card; int ch = pc300chan->channel; volatile pcsca_bd_t __iomem * ptdescr; - struct net_device_stats *stats = hdlc_stats(pc300dev->dev); + struct net_device_stats *stats = &pc300dev->dev->stats; int rx_len, rx_aux; volatile unsigned char status; unsigned short first_bd = pc300chan->rx_first_bd; @@ -917,7 +917,7 @@ static int cpc_tty_send_to_card(pc300dev_t *dev,void* buf, int len) pc300ch_t *chan = (pc300ch_t *)dev->chan; pc300_t *card = (pc300_t *)chan->card; int ch = chan->channel; - struct net_device_stats *stats = hdlc_stats(dev->dev); + struct net_device_stats *stats = &dev->dev->stats; unsigned long flags; volatile pcsca_bd_t __iomem *ptdescr; int i, nchar; diff --git a/drivers/net/wan/wanxl.c b/drivers/net/wan/wanxl.c index d4aab8a28b61..a8a5ca0ee6c2 100644 --- a/drivers/net/wan/wanxl.c +++ b/drivers/net/wan/wanxl.c @@ -161,7 +161,6 @@ static inline void wanxl_cable_intr(port_t *port) static inline void wanxl_tx_intr(port_t *port) { struct net_device *dev = port->dev; - struct net_device_stats *stats = hdlc_stats(dev); while (1) { desc_t *desc = &get_status(port)->tx_descs[port->tx_in]; struct sk_buff *skb = port->tx_skbs[port->tx_in]; @@ -173,13 +172,13 @@ static inline void wanxl_tx_intr(port_t *port) return; case PACKET_UNDERRUN: - stats->tx_errors++; - stats->tx_fifo_errors++; + dev->stats.tx_errors++; + dev->stats.tx_fifo_errors++; break; default: - stats->tx_packets++; - stats->tx_bytes += skb->len; + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; } desc->stat = PACKET_EMPTY; /* Free descriptor */ pci_unmap_single(port->card->pdev, desc->address, skb->len, @@ -205,10 +204,9 @@ static inline void wanxl_rx_intr(card_t *card) port_t *port = &card->ports[desc->stat & PACKET_PORT_MASK]; struct net_device *dev = port->dev; - struct net_device_stats *stats = hdlc_stats(dev); if (!skb) - stats->rx_dropped++; + dev->stats.rx_dropped++; else { pci_unmap_single(card->pdev, desc->address, BUFFER_LENGTH, @@ -220,8 +218,8 @@ static inline void wanxl_rx_intr(card_t *card) skb->len); debug_frame(skb); #endif - stats->rx_packets++; - stats->rx_bytes += skb->len; + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; dev->last_rx = jiffies; skb->protocol = hdlc_type_trans(skb, dev); netif_rx(skb); @@ -468,13 +466,13 @@ static int wanxl_close(struct net_device *dev) static struct net_device_stats *wanxl_get_stats(struct net_device *dev) { - struct net_device_stats *stats = hdlc_stats(dev); port_t *port = dev_to_port(dev); - stats->rx_over_errors = get_status(port)->rx_overruns; - stats->rx_frame_errors = get_status(port)->rx_frame_errors; - stats->rx_errors = stats->rx_over_errors + stats->rx_frame_errors; - return stats; + dev->stats.rx_over_errors = get_status(port)->rx_overruns; + dev->stats.rx_frame_errors = get_status(port)->rx_frame_errors; + dev->stats.rx_errors = dev->stats.rx_over_errors + + dev->stats.rx_frame_errors; + return &dev->stats; } diff --git a/include/linux/hdlc.h b/include/linux/hdlc.h index 6115545a5b9c..c59769693bee 100644 --- a/include/linux/hdlc.h +++ b/include/linux/hdlc.h @@ -45,7 +45,6 @@ struct hdlc_proto { /* Pointed to by dev->priv */ typedef struct hdlc_device { - struct net_device_stats stats; /* used by HDLC layer to take control over HDLC device from hw driver*/ int (*attach)(struct net_device *dev, unsigned short encoding, unsigned short parity); @@ -109,12 +108,6 @@ int attach_hdlc_protocol(struct net_device *dev, struct hdlc_proto *proto, /* May be used by hardware driver to gain control over HDLC device */ void detach_hdlc_protocol(struct net_device *dev); -static __inline__ struct net_device_stats *hdlc_stats(struct net_device *dev) -{ - return &dev_to_hdlc(dev)->stats; -} - - static __inline__ __be16 hdlc_type_trans(struct sk_buff *skb, struct net_device *dev) { -- cgit v1.2.3 From eca9ebac651f774d8b10fce7c5d173c3c3d3394f Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sat, 5 Jul 2008 21:26:13 -0700 Subject: net: Add GARP applicant-only participant Add an implementation of the GARP (Generic Attribute Registration Protocol) applicant-only participant. This will be used by the following patch to add GVRP support to the VLAN code. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 + include/net/garp.h | 127 ++++++++++ net/802/Kconfig | 4 + net/802/Makefile | 1 + net/802/garp.c | 633 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 767 insertions(+) create mode 100644 include/net/garp.h create mode 100644 net/802/garp.c (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 56dadb528f67..e009c6fbf5cd 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -740,6 +740,8 @@ struct net_device struct net_bridge_port *br_port; /* macvlan */ struct macvlan_port *macvlan_port; + /* GARP */ + struct garp_port *garp_port; /* class/net/name entry */ struct device dev; diff --git a/include/net/garp.h b/include/net/garp.h new file mode 100644 index 000000000000..73c772395f5b --- /dev/null +++ b/include/net/garp.h @@ -0,0 +1,127 @@ +#ifndef _NET_GARP_H +#define _NET_GARP_H + +#include + +#define GARP_PROTOCOL_ID 0x1 +#define GARP_END_MARK 0x0 + +struct garp_pdu_hdr { + __be16 protocol; +}; + +struct garp_msg_hdr { + u8 attrtype; +}; + +enum garp_attr_event { + GARP_LEAVE_ALL, + GARP_JOIN_EMPTY, + GARP_JOIN_IN, + GARP_LEAVE_EMPTY, + GARP_LEAVE_IN, + GARP_EMPTY, +}; + +struct garp_attr_hdr { + u8 len; + u8 event; + u8 data[]; +}; + +struct garp_skb_cb { + u8 cur_type; +}; + +static inline struct garp_skb_cb *garp_cb(struct sk_buff *skb) +{ + BUILD_BUG_ON(sizeof(struct garp_skb_cb) > + FIELD_SIZEOF(struct sk_buff, cb)); + return (struct garp_skb_cb *)skb->cb; +} + +enum garp_applicant_state { + GARP_APPLICANT_INVALID, + GARP_APPLICANT_VA, + GARP_APPLICANT_AA, + GARP_APPLICANT_QA, + GARP_APPLICANT_LA, + GARP_APPLICANT_VP, + GARP_APPLICANT_AP, + GARP_APPLICANT_QP, + GARP_APPLICANT_VO, + GARP_APPLICANT_AO, + GARP_APPLICANT_QO, + __GARP_APPLICANT_MAX +}; +#define GARP_APPLICANT_MAX (__GARP_APPLICANT_MAX - 1) + +enum garp_event { + GARP_EVENT_REQ_JOIN, + GARP_EVENT_REQ_LEAVE, + GARP_EVENT_R_JOIN_IN, + GARP_EVENT_R_JOIN_EMPTY, + GARP_EVENT_R_EMPTY, + GARP_EVENT_R_LEAVE_IN, + GARP_EVENT_R_LEAVE_EMPTY, + GARP_EVENT_TRANSMIT_PDU, + __GARP_EVENT_MAX +}; +#define GARP_EVENT_MAX (__GARP_EVENT_MAX - 1) + +enum garp_action { + GARP_ACTION_NONE, + GARP_ACTION_S_JOIN_IN, + GARP_ACTION_S_LEAVE_EMPTY, +}; + +struct garp_attr { + struct rb_node node; + enum garp_applicant_state state; + u8 type; + u8 dlen; + unsigned char data[]; +}; + +enum garp_applications { + __GARP_APPLICATION_MAX +}; +#define GARP_APPLICATION_MAX (__GARP_APPLICATION_MAX - 1) + +struct garp_application { + enum garp_applications type; + unsigned int maxattr; + struct stp_proto proto; +}; + +struct garp_applicant { + struct garp_application *app; + struct net_device *dev; + struct timer_list join_timer; + + spinlock_t lock; + struct sk_buff_head queue; + struct sk_buff *pdu; + struct rb_root gid; +}; + +struct garp_port { + struct garp_applicant *applicants[GARP_APPLICATION_MAX + 1]; +}; + +extern int garp_register_application(struct garp_application *app); +extern void garp_unregister_application(struct garp_application *app); + +extern int garp_init_applicant(struct net_device *dev, + struct garp_application *app); +extern void garp_uninit_applicant(struct net_device *dev, + struct garp_application *app); + +extern int garp_request_join(const struct net_device *dev, + const struct garp_application *app, + const void *data, u8 len, u8 type); +extern void garp_request_leave(const struct net_device *dev, + const struct garp_application *app, + const void *data, u8 len, u8 type); + +#endif /* _NET_GARP_H */ diff --git a/net/802/Kconfig b/net/802/Kconfig index 01cb0943626b..be33d27c8e69 100644 --- a/net/802/Kconfig +++ b/net/802/Kconfig @@ -1,3 +1,7 @@ config STP tristate select LLC + +config GARP + tristate + select STP diff --git a/net/802/Makefile b/net/802/Makefile index c441d895ac25..7893d679910c 100644 --- a/net/802/Makefile +++ b/net/802/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_HIPPI) += hippi.o obj-$(CONFIG_IPX) += p8022.o psnap.o p8023.o obj-$(CONFIG_ATALK) += p8022.o psnap.o obj-$(CONFIG_STP) += stp.o +obj-$(CONFIG_GARP) += garp.o diff --git a/net/802/garp.c b/net/802/garp.c new file mode 100644 index 000000000000..3b78f7b74fd4 --- /dev/null +++ b/net/802/garp.c @@ -0,0 +1,633 @@ +/* + * IEEE 802.1D Generic Attribute Registration Protocol (GARP) + * + * Copyright (c) 2008 Patrick McHardy + * + * 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 +#include + +static unsigned int garp_join_time __read_mostly = 200; +module_param(garp_join_time, uint, 0644); +MODULE_PARM_DESC(garp_join_time, "Join time in ms (default 200ms)"); +MODULE_LICENSE("GPL"); + +static const struct garp_state_trans { + u8 state; + u8 action; +} garp_applicant_state_table[GARP_APPLICANT_MAX + 1][GARP_EVENT_MAX + 1] = { + [GARP_APPLICANT_VA] = { + [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_AA, + .action = GARP_ACTION_S_JOIN_IN }, + [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_AA }, + [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VA }, + [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VA }, + [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VA }, + [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, + [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_LA }, + }, + [GARP_APPLICANT_AA] = { + [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_QA, + .action = GARP_ACTION_S_JOIN_IN }, + [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QA }, + [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VA }, + [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VA }, + [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VA }, + [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, + [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_LA }, + }, + [GARP_APPLICANT_QA] = { + [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID }, + [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QA }, + [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VA }, + [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VA }, + [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, + [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_LA }, + }, + [GARP_APPLICANT_LA] = { + [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_VO, + .action = GARP_ACTION_S_LEAVE_EMPTY }, + [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_LA }, + [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO }, + [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_LA }, + [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_LA }, + [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO }, + [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_VA }, + [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID }, + }, + [GARP_APPLICANT_VP] = { + [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_AA, + .action = GARP_ACTION_S_JOIN_IN }, + [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_AP }, + [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, + [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_VO }, + }, + [GARP_APPLICANT_AP] = { + [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_QA, + .action = GARP_ACTION_S_JOIN_IN }, + [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QP }, + [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, + [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_AO }, + }, + [GARP_APPLICANT_QP] = { + [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID }, + [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QP }, + [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_INVALID }, + [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_QO }, + }, + [GARP_APPLICANT_VO] = { + [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID }, + [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_AO }, + [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO }, + [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VO }, + [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VO }, + [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO }, + [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_VP }, + [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID }, + }, + [GARP_APPLICANT_AO] = { + [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID }, + [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QO }, + [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO }, + [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VO }, + [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VO }, + [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO }, + [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_AP }, + [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID }, + }, + [GARP_APPLICANT_QO] = { + [GARP_EVENT_TRANSMIT_PDU] = { .state = GARP_APPLICANT_INVALID }, + [GARP_EVENT_R_JOIN_IN] = { .state = GARP_APPLICANT_QO }, + [GARP_EVENT_R_JOIN_EMPTY] = { .state = GARP_APPLICANT_VO }, + [GARP_EVENT_R_EMPTY] = { .state = GARP_APPLICANT_VO }, + [GARP_EVENT_R_LEAVE_IN] = { .state = GARP_APPLICANT_VO }, + [GARP_EVENT_R_LEAVE_EMPTY] = { .state = GARP_APPLICANT_VO }, + [GARP_EVENT_REQ_JOIN] = { .state = GARP_APPLICANT_QP }, + [GARP_EVENT_REQ_LEAVE] = { .state = GARP_APPLICANT_INVALID }, + }, +}; + +static int garp_attr_cmp(const struct garp_attr *attr, + const void *data, u8 len, u8 type) +{ + if (attr->type != type) + return attr->type - type; + if (attr->dlen != len) + return attr->dlen - len; + return memcmp(attr->data, data, len); +} + +static struct garp_attr *garp_attr_lookup(const struct garp_applicant *app, + const void *data, u8 len, u8 type) +{ + struct rb_node *parent = app->gid.rb_node; + struct garp_attr *attr; + int d; + + while (parent) { + attr = rb_entry(parent, struct garp_attr, node); + d = garp_attr_cmp(attr, data, len, type); + if (d < 0) + parent = parent->rb_left; + else if (d > 0) + parent = parent->rb_right; + else + return attr; + } + return NULL; +} + +static void garp_attr_insert(struct garp_applicant *app, struct garp_attr *new) +{ + struct rb_node *parent = NULL, **p = &app->gid.rb_node; + struct garp_attr *attr; + int d; + + while (*p) { + parent = *p; + attr = rb_entry(parent, struct garp_attr, node); + d = garp_attr_cmp(attr, new->data, new->dlen, new->type); + if (d < 0) + p = &parent->rb_left; + else if (d > 0) + p = &parent->rb_right; + } + rb_link_node(&new->node, parent, p); + rb_insert_color(&new->node, &app->gid); +} + +static struct garp_attr *garp_attr_create(struct garp_applicant *app, + const void *data, u8 len, u8 type) +{ + struct garp_attr *attr; + + attr = kmalloc(sizeof(*attr) + len, GFP_ATOMIC); + if (!attr) + return attr; + attr->state = GARP_APPLICANT_VO; + attr->type = type; + attr->dlen = len; + memcpy(attr->data, data, len); + garp_attr_insert(app, attr); + return attr; +} + +static void garp_attr_destroy(struct garp_applicant *app, struct garp_attr *attr) +{ + rb_erase(&attr->node, &app->gid); + kfree(attr); +} + +static int garp_pdu_init(struct garp_applicant *app) +{ + struct sk_buff *skb; + struct garp_pdu_hdr *gp; + +#define LLC_RESERVE sizeof(struct llc_pdu_un) + skb = alloc_skb(app->dev->mtu + LL_RESERVED_SPACE(app->dev), + GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + skb->dev = app->dev; + skb->protocol = htons(ETH_P_802_2); + skb_reserve(skb, LL_RESERVED_SPACE(app->dev) + LLC_RESERVE); + + gp = (struct garp_pdu_hdr *)__skb_put(skb, sizeof(*gp)); + put_unaligned(htons(GARP_PROTOCOL_ID), &gp->protocol); + + app->pdu = skb; + return 0; +} + +static int garp_pdu_append_end_mark(struct garp_applicant *app) +{ + if (skb_tailroom(app->pdu) < sizeof(u8)) + return -1; + *(u8 *)__skb_put(app->pdu, sizeof(u8)) = GARP_END_MARK; + return 0; +} + +static void garp_pdu_queue(struct garp_applicant *app) +{ + if (!app->pdu) + return; + + garp_pdu_append_end_mark(app); + garp_pdu_append_end_mark(app); + + llc_pdu_header_init(app->pdu, LLC_PDU_TYPE_U, LLC_SAP_BSPAN, + LLC_SAP_BSPAN, LLC_PDU_CMD); + llc_pdu_init_as_ui_cmd(app->pdu); + llc_mac_hdr_init(app->pdu, app->dev->dev_addr, + app->app->proto.group_address); + + skb_queue_tail(&app->queue, app->pdu); + app->pdu = NULL; +} + +static void garp_queue_xmit(struct garp_applicant *app) +{ + struct sk_buff *skb; + + while ((skb = skb_dequeue(&app->queue))) + dev_queue_xmit(skb); +} + +static int garp_pdu_append_msg(struct garp_applicant *app, u8 attrtype) +{ + struct garp_msg_hdr *gm; + + if (skb_tailroom(app->pdu) < sizeof(*gm)) + return -1; + gm = (struct garp_msg_hdr *)__skb_put(app->pdu, sizeof(*gm)); + gm->attrtype = attrtype; + garp_cb(app->pdu)->cur_type = attrtype; + return 0; +} + +static int garp_pdu_append_attr(struct garp_applicant *app, + const struct garp_attr *attr, + enum garp_attr_event event) +{ + struct garp_attr_hdr *ga; + unsigned int len; + int err; +again: + if (!app->pdu) { + err = garp_pdu_init(app); + if (err < 0) + return err; + } + + if (garp_cb(app->pdu)->cur_type != attr->type) { + if (garp_cb(app->pdu)->cur_type && + garp_pdu_append_end_mark(app) < 0) + goto queue; + if (garp_pdu_append_msg(app, attr->type) < 0) + goto queue; + } + + len = sizeof(*ga) + attr->dlen; + if (skb_tailroom(app->pdu) < len) + goto queue; + ga = (struct garp_attr_hdr *)__skb_put(app->pdu, len); + ga->len = len; + ga->event = event; + memcpy(ga->data, attr->data, attr->dlen); + return 0; + +queue: + garp_pdu_queue(app); + goto again; +} + +static void garp_attr_event(struct garp_applicant *app, + struct garp_attr *attr, enum garp_event event) +{ + enum garp_applicant_state state; + + state = garp_applicant_state_table[attr->state][event].state; + if (state == GARP_APPLICANT_INVALID) + return; + + switch (garp_applicant_state_table[attr->state][event].action) { + case GARP_ACTION_NONE: + break; + case GARP_ACTION_S_JOIN_IN: + garp_pdu_append_attr(app, attr, GARP_JOIN_IN); + break; + case GARP_ACTION_S_LEAVE_EMPTY: + garp_pdu_append_attr(app, attr, GARP_LEAVE_EMPTY); + /* As a pure applicant, sending a leave message implies that + * the attribute was unregistered and can be destroyed. */ + garp_attr_destroy(app, attr); + return; + default: + WARN_ON(1); + } + + attr->state = state; +} + +int garp_request_join(const struct net_device *dev, + const struct garp_application *appl, + const void *data, u8 len, u8 type) +{ + struct garp_port *port = dev->garp_port; + struct garp_applicant *app = port->applicants[appl->type]; + struct garp_attr *attr; + + spin_lock_bh(&app->lock); + attr = garp_attr_create(app, data, len, type); + if (!attr) { + spin_unlock_bh(&app->lock); + return -ENOMEM; + } + garp_attr_event(app, attr, GARP_EVENT_REQ_JOIN); + spin_unlock_bh(&app->lock); + return 0; +} +EXPORT_SYMBOL_GPL(garp_request_join); + +void garp_request_leave(const struct net_device *dev, + const struct garp_application *appl, + const void *data, u8 len, u8 type) +{ + struct garp_port *port = dev->garp_port; + struct garp_applicant *app = port->applicants[appl->type]; + struct garp_attr *attr; + + spin_lock_bh(&app->lock); + attr = garp_attr_lookup(app, data, len, type); + if (!attr) { + spin_unlock_bh(&app->lock); + return; + } + garp_attr_event(app, attr, GARP_EVENT_REQ_LEAVE); + spin_unlock_bh(&app->lock); +} +EXPORT_SYMBOL_GPL(garp_request_leave); + +static void garp_gid_event(struct garp_applicant *app, enum garp_event event) +{ + struct rb_node *node, *next; + struct garp_attr *attr; + + for (node = rb_first(&app->gid); + next = node ? rb_next(node) : NULL, node != NULL; + node = next) { + attr = rb_entry(node, struct garp_attr, node); + garp_attr_event(app, attr, event); + } +} + +static void garp_join_timer_arm(struct garp_applicant *app) +{ + unsigned long delay; + + delay = (u64)msecs_to_jiffies(garp_join_time) * net_random() >> 32; + mod_timer(&app->join_timer, jiffies + delay); +} + +static void garp_join_timer(unsigned long data) +{ + struct garp_applicant *app = (struct garp_applicant *)data; + + spin_lock(&app->lock); + garp_gid_event(app, GARP_EVENT_TRANSMIT_PDU); + garp_pdu_queue(app); + spin_unlock(&app->lock); + + garp_queue_xmit(app); + garp_join_timer_arm(app); +} + +static int garp_pdu_parse_end_mark(struct sk_buff *skb) +{ + if (!pskb_may_pull(skb, sizeof(u8))) + return -1; + if (*skb->data == GARP_END_MARK) { + skb_pull(skb, sizeof(u8)); + return -1; + } + return 0; +} + +static int garp_pdu_parse_attr(struct garp_applicant *app, struct sk_buff *skb, + u8 attrtype) +{ + const struct garp_attr_hdr *ga; + struct garp_attr *attr; + enum garp_event event; + unsigned int dlen; + + if (!pskb_may_pull(skb, sizeof(*ga))) + return -1; + ga = (struct garp_attr_hdr *)skb->data; + if (ga->len < sizeof(*ga)) + return -1; + + if (!pskb_may_pull(skb, ga->len)) + return -1; + skb_pull(skb, ga->len); + dlen = sizeof(*ga) - ga->len; + + if (attrtype > app->app->maxattr) + return 0; + + switch (ga->event) { + case GARP_LEAVE_ALL: + if (dlen != 0) + return -1; + garp_gid_event(app, GARP_EVENT_R_LEAVE_EMPTY); + return 0; + case GARP_JOIN_EMPTY: + event = GARP_EVENT_R_JOIN_EMPTY; + break; + case GARP_JOIN_IN: + event = GARP_EVENT_R_JOIN_IN; + break; + case GARP_LEAVE_EMPTY: + event = GARP_EVENT_R_LEAVE_EMPTY; + break; + case GARP_EMPTY: + event = GARP_EVENT_R_EMPTY; + break; + default: + return 0; + } + + if (dlen == 0) + return -1; + attr = garp_attr_lookup(app, ga->data, dlen, attrtype); + if (attr == NULL) + return 0; + garp_attr_event(app, attr, event); + return 0; +} + +static int garp_pdu_parse_msg(struct garp_applicant *app, struct sk_buff *skb) +{ + const struct garp_msg_hdr *gm; + + if (!pskb_may_pull(skb, sizeof(*gm))) + return -1; + gm = (struct garp_msg_hdr *)skb->data; + if (gm->attrtype == 0) + return -1; + skb_pull(skb, sizeof(*gm)); + + while (skb->len > 0) { + if (garp_pdu_parse_attr(app, skb, gm->attrtype) < 0) + return -1; + if (garp_pdu_parse_end_mark(skb) < 0) + break; + } + return 0; +} + +static void garp_pdu_rcv(const struct stp_proto *proto, struct sk_buff *skb, + struct net_device *dev) +{ + struct garp_application *appl = proto->data; + struct garp_port *port; + struct garp_applicant *app; + const struct garp_pdu_hdr *gp; + + port = rcu_dereference(dev->garp_port); + if (!port) + goto err; + app = rcu_dereference(port->applicants[appl->type]); + if (!app) + goto err; + + if (!pskb_may_pull(skb, sizeof(*gp))) + goto err; + gp = (struct garp_pdu_hdr *)skb->data; + if (get_unaligned(&gp->protocol) != htons(GARP_PROTOCOL_ID)) + goto err; + skb_pull(skb, sizeof(*gp)); + + spin_lock(&app->lock); + while (skb->len > 0) { + if (garp_pdu_parse_msg(app, skb) < 0) + break; + if (garp_pdu_parse_end_mark(skb) < 0) + break; + } + spin_unlock(&app->lock); +err: + kfree_skb(skb); +} + +static int garp_init_port(struct net_device *dev) +{ + struct garp_port *port; + + port = kzalloc(sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; + rcu_assign_pointer(dev->garp_port, port); + return 0; +} + +static void garp_release_port(struct net_device *dev) +{ + struct garp_port *port = dev->garp_port; + unsigned int i; + + for (i = 0; i <= GARP_APPLICATION_MAX; i++) { + if (port->applicants[i]) + return; + } + rcu_assign_pointer(dev->garp_port, NULL); + synchronize_rcu(); + kfree(port); +} + +int garp_init_applicant(struct net_device *dev, struct garp_application *appl) +{ + struct garp_applicant *app; + int err; + + ASSERT_RTNL(); + + if (!dev->garp_port) { + err = garp_init_port(dev); + if (err < 0) + goto err1; + } + + err = -ENOMEM; + app = kzalloc(sizeof(*app), GFP_KERNEL); + if (!app) + goto err2; + + err = dev_mc_add(dev, appl->proto.group_address, ETH_ALEN, 0); + if (err < 0) + goto err3; + + app->dev = dev; + app->app = appl; + app->gid = RB_ROOT; + spin_lock_init(&app->lock); + skb_queue_head_init(&app->queue); + rcu_assign_pointer(dev->garp_port->applicants[appl->type], app); + setup_timer(&app->join_timer, garp_join_timer, (unsigned long)app); + garp_join_timer_arm(app); + return 0; + +err3: + kfree(app); +err2: + garp_release_port(dev); +err1: + return err; +} +EXPORT_SYMBOL_GPL(garp_init_applicant); + +void garp_uninit_applicant(struct net_device *dev, struct garp_application *appl) +{ + struct garp_port *port = dev->garp_port; + struct garp_applicant *app = port->applicants[appl->type]; + + ASSERT_RTNL(); + + rcu_assign_pointer(port->applicants[appl->type], NULL); + synchronize_rcu(); + + /* Delete timer and generate a final TRANSMIT_PDU event to flush out + * all pending messages before the applicant is gone. */ + del_timer_sync(&app->join_timer); + garp_gid_event(app, GARP_EVENT_TRANSMIT_PDU); + garp_pdu_queue(app); + garp_queue_xmit(app); + + dev_mc_delete(dev, appl->proto.group_address, ETH_ALEN, 0); + kfree(app); + garp_release_port(dev); +} +EXPORT_SYMBOL_GPL(garp_uninit_applicant); + +int garp_register_application(struct garp_application *appl) +{ + appl->proto.rcv = garp_pdu_rcv; + appl->proto.data = appl; + return stp_proto_register(&appl->proto); +} +EXPORT_SYMBOL_GPL(garp_register_application); + +void garp_unregister_application(struct garp_application *appl) +{ + stp_proto_unregister(&appl->proto); +} +EXPORT_SYMBOL_GPL(garp_unregister_application); -- cgit v1.2.3 From 70c03b49b80ba3634958acc31853771019c0ebd3 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sat, 5 Jul 2008 21:26:57 -0700 Subject: vlan: Add GVRP support Add GVRP support for dynamically registering VLANs with switches. By default GVRP is disabled because we only support the applicant-only participant model, which means it should not be enabled on vlans that are members of a bridge. Since there is currently no way to cleanly determine that, the user is responsible for enabling it. The code is pretty small and low impact, its wrapped in a config option though because it depends on the GARP implementation and the STP core. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/if_vlan.h | 1 + include/net/garp.h | 1 + net/8021q/Kconfig | 10 ++++++++ net/8021q/Makefile | 9 +++---- net/8021q/vlan.c | 23 ++++++++++++++--- net/8021q/vlan.h | 16 ++++++++++++ net/8021q/vlan_dev.c | 18 +++++++++++-- net/8021q/vlan_gvrp.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++ net/8021q/vlan_netlink.c | 3 ++- 9 files changed, 134 insertions(+), 13 deletions(-) create mode 100644 net/8021q/vlan_gvrp.c (limited to 'include/linux') diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 15ace02b7b24..5190452ac7dc 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -402,6 +402,7 @@ enum vlan_ioctl_cmds { enum vlan_flags { VLAN_FLAG_REORDER_HDR = 0x1, + VLAN_FLAG_GVRP = 0x2, }; enum vlan_name_types { diff --git a/include/net/garp.h b/include/net/garp.h index 73c772395f5b..825f172caba9 100644 --- a/include/net/garp.h +++ b/include/net/garp.h @@ -84,6 +84,7 @@ struct garp_attr { }; enum garp_applications { + GARP_APPLICATION_GVRP, __GARP_APPLICATION_MAX }; #define GARP_APPLICATION_MAX (__GARP_APPLICATION_MAX - 1) diff --git a/net/8021q/Kconfig b/net/8021q/Kconfig index c4a382e450e2..fa073a54963e 100644 --- a/net/8021q/Kconfig +++ b/net/8021q/Kconfig @@ -17,3 +17,13 @@ config VLAN_8021Q will be called 8021q. If unsure, say N. + +config VLAN_8021Q_GVRP + bool "GVRP (GARP VLAN Registration Protocol) support" + depends on VLAN_8021Q + select GARP + help + Select this to enable GVRP end-system support. GVRP is used for + automatic propagation of registered VLANs to switches. + + If unsure, say N. diff --git a/net/8021q/Makefile b/net/8021q/Makefile index 10ca7f486c3a..3006e9ed7b08 100644 --- a/net/8021q/Makefile +++ b/net/8021q/Makefile @@ -4,9 +4,6 @@ obj-$(CONFIG_VLAN_8021Q) += 8021q.o -8021q-objs := vlan.o vlan_dev.o vlan_netlink.o - -ifeq ($(CONFIG_PROC_FS),y) -8021q-objs += vlanproc.o -endif - +8021q-y := vlan.o vlan_dev.o vlan_netlink.o +8021q-$(CONFIG_VLAN_8021Q_GVRP) += vlan_gvrp.o +8021q-$(CONFIG_PROC_FS) += vlanproc.o diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 8cae2daeb1cc..b529110c9355 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -169,6 +169,8 @@ void unregister_vlan_dev(struct net_device *dev) /* If the group is now empty, kill off the group. */ if (grp->nr_vlans == 0) { + vlan_gvrp_uninit_applicant(real_dev); + if (real_dev->features & NETIF_F_HW_VLAN_RX) real_dev->vlan_rx_register(real_dev, NULL); @@ -249,15 +251,18 @@ int register_vlan_dev(struct net_device *dev) ngrp = grp = vlan_group_alloc(real_dev); if (!grp) return -ENOBUFS; + err = vlan_gvrp_init_applicant(real_dev); + if (err < 0) + goto out_free_group; } err = vlan_group_prealloc_vid(grp, vlan_id); if (err < 0) - goto out_free_group; + goto out_uninit_applicant; err = register_netdevice(dev); if (err < 0) - goto out_free_group; + goto out_uninit_applicant; /* Account for reference in struct vlan_dev_info */ dev_hold(real_dev); @@ -278,6 +283,9 @@ int register_vlan_dev(struct net_device *dev) return 0; +out_uninit_applicant: + if (ngrp) + vlan_gvrp_uninit_applicant(real_dev); out_free_group: if (ngrp) vlan_group_free(ngrp); @@ -713,14 +721,20 @@ static int __init vlan_proto_init(void) if (err < 0) goto err2; - err = vlan_netlink_init(); + err = vlan_gvrp_init(); if (err < 0) goto err3; + err = vlan_netlink_init(); + if (err < 0) + goto err4; + dev_add_pack(&vlan_packet_type); vlan_ioctl_set(vlan_ioctl_handler); return 0; +err4: + vlan_gvrp_uninit(); err3: unregister_netdevice_notifier(&vlan_notifier_block); err2: @@ -745,8 +759,9 @@ static void __exit vlan_cleanup_module(void) BUG_ON(!hlist_empty(&vlan_group_hash[i])); unregister_pernet_gen_device(vlan_net_id, &vlan_net_ops); - synchronize_net(); + + vlan_gvrp_uninit(); } module_init(vlan_proto_init); diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h index 639e2544a804..097b2e04c928 100644 --- a/net/8021q/vlan.h +++ b/net/8021q/vlan.h @@ -37,6 +37,22 @@ void vlan_setup(struct net_device *dev); int register_vlan_dev(struct net_device *dev); void unregister_vlan_dev(struct net_device *dev); +#ifdef CONFIG_VLAN_8021Q_GVRP +extern int vlan_gvrp_request_join(const struct net_device *dev); +extern void vlan_gvrp_request_leave(const struct net_device *dev); +extern int vlan_gvrp_init_applicant(struct net_device *dev); +extern void vlan_gvrp_uninit_applicant(struct net_device *dev); +extern int vlan_gvrp_init(void); +extern void vlan_gvrp_uninit(void); +#else +static inline int vlan_gvrp_request_join(const struct net_device *dev) { return 0; } +static inline void vlan_gvrp_request_leave(const struct net_device *dev) {} +static inline int vlan_gvrp_init_applicant(struct net_device *dev) { return 0; } +static inline void vlan_gvrp_uninit_applicant(struct net_device *dev) {} +static inline int vlan_gvrp_init(void) { return 0; } +static inline void vlan_gvrp_uninit(void) {} +#endif + int vlan_netlink_init(void); void vlan_netlink_fini(void); diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 76c665cdab66..a0617bf7cec6 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -512,10 +512,17 @@ int vlan_dev_change_flags(const struct net_device *dev, u32 flags, u32 mask) struct vlan_dev_info *vlan = vlan_dev_info(dev); u32 old_flags = vlan->flags; - if (mask & ~VLAN_FLAG_REORDER_HDR) + if (mask & ~(VLAN_FLAG_REORDER_HDR | VLAN_FLAG_GVRP)) return -EINVAL; vlan->flags = (old_flags & ~mask) | (flags & mask); + + if (netif_running(dev) && (vlan->flags ^ old_flags) & VLAN_FLAG_GVRP) { + if (vlan->flags & VLAN_FLAG_GVRP) + vlan_gvrp_request_join(dev); + else + vlan_gvrp_request_leave(dev); + } return 0; } @@ -550,12 +557,19 @@ static int vlan_dev_open(struct net_device *dev) if (dev->flags & IFF_PROMISC) dev_set_promiscuity(real_dev, 1); + if (vlan->flags & VLAN_FLAG_GVRP) + vlan_gvrp_request_join(dev); + return 0; } static int vlan_dev_stop(struct net_device *dev) { - struct net_device *real_dev = vlan_dev_info(dev)->real_dev; + struct vlan_dev_info *vlan = vlan_dev_info(dev); + struct net_device *real_dev = vlan->real_dev; + + if (vlan->flags & VLAN_FLAG_GVRP) + vlan_gvrp_request_leave(dev); dev_mc_unsync(real_dev, dev); dev_unicast_unsync(real_dev, dev); diff --git a/net/8021q/vlan_gvrp.c b/net/8021q/vlan_gvrp.c new file mode 100644 index 000000000000..db9781608362 --- /dev/null +++ b/net/8021q/vlan_gvrp.c @@ -0,0 +1,66 @@ +/* + * IEEE 802.1Q GARP VLAN Registration Protocol (GVRP) + * + * Copyright (c) 2008 Patrick McHardy + * + * 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 "vlan.h" + +#define GARP_GVRP_ADDRESS { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x21 } + +enum gvrp_attributes { + GVRP_ATTR_INVALID, + GVRP_ATTR_VID, + __GVRP_ATTR_MAX +}; +#define GVRP_ATTR_MAX (__GVRP_ATTR_MAX - 1) + +static struct garp_application vlan_gvrp_app __read_mostly = { + .proto.group_address = GARP_GVRP_ADDRESS, + .maxattr = GVRP_ATTR_MAX, + .type = GARP_APPLICATION_GVRP, +}; + +int vlan_gvrp_request_join(const struct net_device *dev) +{ + const struct vlan_dev_info *vlan = vlan_dev_info(dev); + __be16 vid = htons(vlan->vlan_id); + + return garp_request_join(vlan->real_dev, &vlan_gvrp_app, + &vid, sizeof(vid), GVRP_ATTR_VID); +} + +void vlan_gvrp_request_leave(const struct net_device *dev) +{ + const struct vlan_dev_info *vlan = vlan_dev_info(dev); + __be16 vid = htons(vlan->vlan_id); + + garp_request_leave(vlan->real_dev, &vlan_gvrp_app, + &vid, sizeof(vid), GVRP_ATTR_VID); +} + +int vlan_gvrp_init_applicant(struct net_device *dev) +{ + return garp_init_applicant(dev, &vlan_gvrp_app); +} + +void vlan_gvrp_uninit_applicant(struct net_device *dev) +{ + garp_uninit_applicant(dev, &vlan_gvrp_app); +} + +int __init vlan_gvrp_init(void) +{ + return garp_register_application(&vlan_gvrp_app); +} + +void vlan_gvrp_uninit(void) +{ + garp_unregister_application(&vlan_gvrp_app); +} diff --git a/net/8021q/vlan_netlink.c b/net/8021q/vlan_netlink.c index fd7cb195d53f..e9c91dcecc9b 100644 --- a/net/8021q/vlan_netlink.c +++ b/net/8021q/vlan_netlink.c @@ -59,7 +59,8 @@ static int vlan_validate(struct nlattr *tb[], struct nlattr *data[]) } if (data[IFLA_VLAN_FLAGS]) { flags = nla_data(data[IFLA_VLAN_FLAGS]); - if ((flags->flags & flags->mask) & ~VLAN_FLAG_REORDER_HDR) + if ((flags->flags & flags->mask) & + ~(VLAN_FLAG_REORDER_HDR | VLAN_FLAG_GVRP)) return -EINVAL; } -- cgit v1.2.3 From 73422392734bb68c8ff8bc74ce1bbdc32f1b639a Mon Sep 17 00:00:00 2001 From: Claudio Nieder Date: Mon, 7 Jul 2008 11:56:30 -0400 Subject: Input: add driver for Tabletkiosk Sahara TouchIT-213 touchscreen Signed-off-by: Claudio Nieder Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 11 ++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/touchit213.c | 234 +++++++++++++++++++++++++++++++++ include/linux/serio.h | 1 + 4 files changed, 247 insertions(+) create mode 100644 drivers/input/touchscreen/touchit213.c (limited to 'include/linux') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 4085791e7be2..e57366521572 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -351,4 +351,15 @@ config TOUCHSCREEN_USB_GOTOP bool "GoTop Super_Q2/GogoPen/PenPower tablet device support" if EMBEDDED depends on TOUCHSCREEN_USB_COMPOSITE +config TOUCHSCREEN_TOUCHIT213 + tristate "Sahara TouchIT-213 touchscreen" + select SERIO + help + Say Y here if you have a Sahara TouchIT-213 Tablet PC. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called touchit213. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index d63bcdcd28ec..39a804cd80f1 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o obj-$(CONFIG_TOUCHSCREEN_HTCPEN) += htcpen.o obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o +obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o diff --git a/drivers/input/touchscreen/touchit213.c b/drivers/input/touchscreen/touchit213.c new file mode 100644 index 000000000000..d1297ba19daf --- /dev/null +++ b/drivers/input/touchscreen/touchit213.c @@ -0,0 +1,234 @@ +/* + * Sahara TouchIT-213 serial touchscreen driver + * + * Copyright (c) 2007-2008 Claudio Nieder + * + * Based on Touchright driver (drivers/input/touchscreen/touchright.c) + * Copyright (c) 2006 Rick Koch + * Copyright (c) 2004 Vojtech Pavlik + * and Dan Streetman + */ + +/* + * 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 + +#define DRIVER_DESC "Sahara TouchIT-213 serial touchscreen driver" + +MODULE_AUTHOR("Claudio Nieder "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * Definitions & global arrays. + */ + +/* + * Data is received through COM1 at 9600bit/s,8bit,no parity in packets + * of 5 byte each. + * + * +--------+ +--------+ +--------+ +--------+ +--------+ + * |1000000p| |0xxxxxxx| |0xxxxxxx| |0yyyyyyy| |0yyyyyyy| + * +--------+ +--------+ +--------+ +--------+ +--------+ + * MSB LSB MSB LSB + * + * The value of p is 1 as long as the screen is touched and 0 when + * reporting the location where touching stopped, e.g. where the pen was + * lifted from the screen. + * + * When holding the screen in landscape mode as the BIOS text output is + * presented, x is the horizontal axis with values growing from left to + * right and y is the vertical axis with values growing from top to + * bottom. + * + * When holding the screen in portrait mode with the Sahara logo in its + * correct position, x ist the vertical axis with values growing from + * top to bottom and y is the horizontal axis with values growing from + * right to left. + */ + +#define T213_FORMAT_TOUCH_BIT 0x01 +#define T213_FORMAT_STATUS_BYTE 0x80 +#define T213_FORMAT_STATUS_MASK ~T213_FORMAT_TOUCH_BIT + +/* + * On my Sahara Touch-IT 213 I have observed x values from 0 to 0x7f0 + * and y values from 0x1d to 0x7e9, so the actual measurement is + * probably done with an 11 bit precision. + */ +#define T213_MIN_XC 0 +#define T213_MAX_XC 0x07ff +#define T213_MIN_YC 0 +#define T213_MAX_YC 0x07ff + +/* + * Per-touchscreen data. + */ + +struct touchit213 { + struct input_dev *dev; + struct serio *serio; + int idx; + unsigned char csum; + unsigned char data[5]; + char phys[32]; +}; + +static irqreturn_t touchit213_interrupt(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct touchit213 *touchit213 = serio_get_drvdata(serio); + struct input_dev *dev = touchit213->dev; + + touchit213->data[touchit213->idx] = data; + + switch (touchit213->idx++) { + case 0: + if ((touchit213->data[0] & T213_FORMAT_STATUS_MASK) != + T213_FORMAT_STATUS_BYTE) { + pr_debug("unsynchronized data: 0x%02x\n", data); + touchit213->idx = 0; + } + break; + + case 4: + touchit213->idx = 0; + input_report_abs(dev, ABS_X, + (touchit213->data[1] << 7) | touchit213->data[2]); + input_report_abs(dev, ABS_Y, + (touchit213->data[3] << 7) | touchit213->data[4]); + input_report_key(dev, BTN_TOUCH, + touchit213->data[0] & T213_FORMAT_TOUCH_BIT); + input_sync(dev); + break; + } + + return IRQ_HANDLED; +} + +/* + * touchit213_disconnect() is the opposite of touchit213_connect() + */ + +static void touchit213_disconnect(struct serio *serio) +{ + struct touchit213 *touchit213 = serio_get_drvdata(serio); + + input_get_device(touchit213->dev); + input_unregister_device(touchit213->dev); + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_put_device(touchit213->dev); + kfree(touchit213); +} + +/* + * touchit213_connect() is the routine that is called when someone adds a + * new serio device that supports the Touchright protocol and registers it as + * an input device. + */ + +static int touchit213_connect(struct serio *serio, struct serio_driver *drv) +{ + struct touchit213 *touchit213; + struct input_dev *input_dev; + int err; + + touchit213 = kzalloc(sizeof(struct touchit213), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!touchit213 || !input_dev) { + err = -ENOMEM; + goto fail1; + } + + touchit213->serio = serio; + touchit213->dev = input_dev; + snprintf(touchit213->phys, sizeof(touchit213->phys), + "%s/input0", serio->phys); + + input_dev->name = "Sahara Touch-iT213 Serial TouchScreen"; + input_dev->phys = touchit213->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_TOUCHIT213; + input_dev->id.product = 0; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(touchit213->dev, ABS_X, + T213_MIN_XC, T213_MAX_XC, 0, 0); + input_set_abs_params(touchit213->dev, ABS_Y, + T213_MIN_YC, T213_MAX_YC, 0, 0); + + serio_set_drvdata(serio, touchit213); + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(touchit213->dev); + if (err) + goto fail3; + + return 0; + + fail3: serio_close(serio); + fail2: serio_set_drvdata(serio, NULL); + fail1: input_free_device(input_dev); + kfree(touchit213); + return err; +} + +/* + * The serio driver structure. + */ + +static struct serio_device_id touchit213_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_TOUCHIT213, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, touchit213_serio_ids); + +static struct serio_driver touchit213_drv = { + .driver = { + .name = "touchit213", + }, + .description = DRIVER_DESC, + .id_table = touchit213_serio_ids, + .interrupt = touchit213_interrupt, + .connect = touchit213_connect, + .disconnect = touchit213_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +static int __init touchit213_init(void) +{ + return serio_register_driver(&touchit213_drv); +} + +static void __exit touchit213_exit(void) +{ + serio_unregister_driver(&touchit213_drv); +} + +module_init(touchit213_init); +module_exit(touchit213_exit); diff --git a/include/linux/serio.h b/include/linux/serio.h index 36c6ab81c7e0..48defc4d181e 100644 --- a/include/linux/serio.h +++ b/include/linux/serio.h @@ -213,5 +213,6 @@ static inline void serio_unpin_driver(struct serio *serio) #define SERIO_FUJITSU 0x35 #define SERIO_ZHENHUA 0x36 #define SERIO_INEXIO 0x37 +#define SERIO_TOUCHIT213 0x37 #endif -- cgit v1.2.3 From c6c4f070a61b2b6e5cd317a5fbf25255878688a2 Mon Sep 17 00:00:00 2001 From: Greg KH Date: Thu, 3 Jul 2008 09:49:39 -0700 Subject: PCI: make pci_name use dev_name Also fixes up the sparc code that was assuming this is not a constant. Acked-by: David S. Miller Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman Signed-off-by: Jesse Barnes --- arch/sparc64/kernel/pci.c | 2 +- include/linux/pci.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c index 112b09f16f36..d00a3656c287 100644 --- a/arch/sparc64/kernel/pci.c +++ b/arch/sparc64/kernel/pci.c @@ -408,7 +408,7 @@ struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm, dev->class = class >> 8; dev->revision = class & 0xff; - sprintf(pci_name(dev), "%04x:%02x:%02x.%d", pci_domain_nr(bus), + sprintf(dev->dev.bus_id, "%04x:%02x:%02x.%d", pci_domain_nr(bus), dev->bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn)); if (ofpci_verbose) diff --git a/include/linux/pci.h b/include/linux/pci.h index 96ebaa8d80e3..4c80dc3f2990 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -996,9 +996,9 @@ static inline void pci_set_drvdata(struct pci_dev *pdev, void *data) /* If you want to know what to call your pci_dev, ask this function. * Again, it's a wrapper around the generic device. */ -static inline char *pci_name(struct pci_dev *pdev) +static inline const char *pci_name(struct pci_dev *pdev) { - return pdev->dev.bus_id; + return dev_name(&pdev->dev); } -- cgit v1.2.3 From eb9d0fe40e313c0a74115ef456a2e43a6c8da72f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 7 Jul 2008 03:34:48 +0200 Subject: PCI ACPI: Rework PCI handling of wake-up * Introduce function acpi_pm_device_sleep_wake() for enabling and disabling the system wake-up capability of devices that are power manageable by ACPI. * Introduce function acpi_bus_can_wakeup() allowing other (dependent) subsystems to check if ACPI is able to enable the system wake-up capability of given device. * Introduce callback .sleep_wake() in struct pci_platform_pm_ops and for the ACPI PCI 'driver' make it use acpi_pm_device_sleep_wake(). * Introduce callback .can_wakeup() in struct pci_platform_pm_ops and for the ACPI 'driver' make it use acpi_bus_can_wakeup(). * Move the PME# handlig code out of pci_enable_wake() and split it into two functions, pci_pme_capable() and pci_pme_active(), allowing the caller to check if given device is capable of generating PME# from given power state and to enable/disable the device's PME# functionality, respectively. * Modify pci_enable_wake() to use the new ACPI callbacks and the new PME#-related functions. * Drop the generic .platform_enable_wakeup() callback that is not used any more. * Introduce device_set_wakeup_capable() that will set the power.can_wakeup flag of given device. * Rework PCI device PM initialization so that, if given device is capable of generating wake-up events, either natively through the PME# mechanism, or with the help of the platform, its power.can_wakeup flag is set and its power.should_wakeup flag is unset as appropriate. * Make ACPI set the power.can_wakeup flag for devices found to be wake-up capable by it. * Make the ACPI wake-up code enable/disable GPEs for devices that have the wakeup.flags.prepared flag set (which means that their wake-up power has been enabled). Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/acpi/bus.c | 11 +++ drivers/acpi/glue.c | 2 + drivers/acpi/sleep/main.c | 25 +++++++ drivers/acpi/sleep/wakeup.c | 11 +-- drivers/base/power/sysfs.c | 3 - drivers/pci/pci-acpi.c | 20 ++++++ drivers/pci/pci.c | 169 ++++++++++++++++++++++++++++++++------------ drivers/pci/pci.h | 8 +++ drivers/pci/probe.c | 47 +----------- include/acpi/acpi_bus.h | 2 + include/linux/pm_wakeup.h | 26 ++----- 11 files changed, 208 insertions(+), 116 deletions(-) (limited to 'include/linux') diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index b9b69d9629b5..fc1110d6a078 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -306,6 +306,17 @@ bool acpi_bus_power_manageable(acpi_handle handle) EXPORT_SYMBOL(acpi_bus_power_manageable); +bool acpi_bus_can_wakeup(acpi_handle handle) +{ + struct acpi_device *device; + int result; + + result = acpi_bus_get_device(handle, &device); + return result ? false : device->wakeup.flags.valid; +} + +EXPORT_SYMBOL(acpi_bus_can_wakeup); + /* -------------------------------------------------------------------------- Event Management -------------------------------------------------------------------------- */ diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 06f8634fe58b..87c5d456e180 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -166,6 +166,8 @@ static int acpi_bind_one(struct device *dev, acpi_handle handle) "firmware_node"); ret = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj, "physical_node"); + if (acpi_dev->wakeup.flags.valid) + device_set_wakeup_capable(dev, true); } return 0; diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c index 4addf8ad50ae..af7f4663deaa 100644 --- a/drivers/acpi/sleep/main.c +++ b/drivers/acpi/sleep/main.c @@ -468,6 +468,31 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p) *d_min_p = d_min; return d_max; } + +/** + * acpi_pm_device_sleep_wake - enable or disable the system wake-up + * capability of given device + * @dev: device to handle + * @enable: 'true' - enable, 'false' - disable the wake-up capability + */ +int acpi_pm_device_sleep_wake(struct device *dev, bool enable) +{ + acpi_handle handle; + struct acpi_device *adev; + + if (!device_may_wakeup(dev)) + return -EINVAL; + + handle = DEVICE_ACPI_HANDLE(dev); + if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &adev))) { + printk(KERN_DEBUG "ACPI handle has no context!\n"); + return -ENODEV; + } + + return enable ? + acpi_enable_wakeup_device_power(adev, acpi_target_sleep_state) : + acpi_disable_wakeup_device_power(adev); +} #endif static void acpi_power_off_prepare(void) diff --git a/drivers/acpi/sleep/wakeup.c b/drivers/acpi/sleep/wakeup.c index 7422a2213944..38655eb132dc 100644 --- a/drivers/acpi/sleep/wakeup.c +++ b/drivers/acpi/sleep/wakeup.c @@ -66,13 +66,15 @@ void acpi_enable_wakeup_device(u8 sleep_state) list_for_each_safe(node, next, &acpi_wakeup_device_list) { struct acpi_device *dev = container_of(node, struct acpi_device, wakeup_list); + if (!dev->wakeup.flags.valid) continue; + /* If users want to disable run-wake GPE, * we only disable it for wake and leave it for runtime */ - if (!dev->wakeup.state.enabled || - sleep_state > (u32) dev->wakeup.sleep_state) { + if ((!dev->wakeup.state.enabled && !dev->wakeup.flags.prepared) + || sleep_state > (u32) dev->wakeup.sleep_state) { if (dev->wakeup.flags.run_wake) { spin_unlock(&acpi_device_lock); /* set_gpe_type will disable GPE, leave it like that */ @@ -110,8 +112,9 @@ void acpi_disable_wakeup_device(u8 sleep_state) if (!dev->wakeup.flags.valid) continue; - if (!dev->wakeup.state.enabled || - sleep_state > (u32) dev->wakeup.sleep_state) { + + if ((!dev->wakeup.state.enabled && !dev->wakeup.flags.prepared) + || sleep_state > (u32) dev->wakeup.sleep_state) { if (dev->wakeup.flags.run_wake) { spin_unlock(&acpi_device_lock); acpi_set_gpe_type(dev->wakeup.gpe_device, diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index d11f74b038db..596aeecfdffe 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -6,9 +6,6 @@ #include #include "power.h" -int (*platform_enable_wakeup)(struct device *dev, int is_on); - - /* * wakeup - Report/change current wakeup option for device * diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 6bc0d8c870af..7764768b6a0e 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -299,10 +299,30 @@ static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state) return error; } +static bool acpi_pci_can_wakeup(struct pci_dev *dev) +{ + acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev); + + return handle ? acpi_bus_can_wakeup(handle) : false; +} + +static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable) +{ + int error = acpi_pm_device_sleep_wake(&dev->dev, enable); + + if (!error) + dev_printk(KERN_INFO, &dev->dev, + "wake-up capability %s by ACPI\n", + enable ? "enabled" : "disabled"); + return error; +} + static struct pci_platform_pm_ops acpi_pci_platform_pm = { .is_manageable = acpi_pci_power_manageable, .set_state = acpi_pci_set_power_state, .choose_state = acpi_pci_choose_state, + .can_wakeup = acpi_pci_can_wakeup, + .sleep_wake = acpi_pci_sleep_wake, }; /* ACPI bus type */ diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 20e28077b96d..a6b1b6f96abc 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -380,7 +380,8 @@ static struct pci_platform_pm_ops *pci_platform_pm; int pci_set_platform_pm(struct pci_platform_pm_ops *ops) { - if (!ops->is_manageable || !ops->set_state || !ops->choose_state) + if (!ops->is_manageable || !ops->set_state || !ops->choose_state + || !ops->sleep_wake || !ops->can_wakeup) return -EINVAL; pci_platform_pm = ops; return 0; @@ -403,6 +404,17 @@ static inline pci_power_t platform_pci_choose_state(struct pci_dev *dev) pci_platform_pm->choose_state(dev) : PCI_POWER_ERROR; } +static inline bool platform_pci_can_wakeup(struct pci_dev *dev) +{ + return pci_platform_pm ? pci_platform_pm->can_wakeup(dev) : false; +} + +static inline int platform_pci_sleep_wake(struct pci_dev *dev, bool enable) +{ + return pci_platform_pm ? + pci_platform_pm->sleep_wake(dev, enable) : -ENODEV; +} + /** * pci_raw_set_power_state - Use PCI PM registers to set the power state of * given PCI device @@ -1035,6 +1047,56 @@ int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state) return pcibios_set_pcie_reset_state(dev, state); } +/** + * pci_pme_capable - check the capability of PCI device to generate PME# + * @dev: PCI device to handle. + * @pm: PCI PM capability offset of the device. + * @state: PCI state from which device will issue PME#. + */ +static bool pci_pme_capable(struct pci_dev *dev, int pm, pci_power_t state) +{ + u16 pmc; + + if (!pm) + return false; + + /* Check device's ability to generate PME# from given state */ + pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc); + + pmc &= PCI_PM_CAP_PME_MASK; + pmc >>= ffs(PCI_PM_CAP_PME_MASK) - 1; /* First bit of mask */ + + return !!(pmc & (1 << state)); +} + +/** + * pci_pme_active - enable or disable PCI device's PME# function + * @dev: PCI device to handle. + * @pm: PCI PM capability offset of the device. + * @enable: 'true' to enable PME# generation; 'false' to disable it. + * + * The caller must verify that the device is capable of generating PME# before + * calling this function with @enable equal to 'true'. + */ +static void pci_pme_active(struct pci_dev *dev, int pm, bool enable) +{ + u16 pmcsr; + + if (!pm) + return; + + pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr); + /* Clear PME_Status by writing 1 to it and enable PME# */ + pmcsr |= PCI_PM_CTRL_PME_STATUS | PCI_PM_CTRL_PME_ENABLE; + if (!enable) + pmcsr &= ~PCI_PM_CTRL_PME_ENABLE; + + pci_write_config_word(dev, pm + PCI_PM_CTRL, pmcsr); + + dev_printk(KERN_INFO, &dev->dev, "PME# %s\n", + enable ? "enabled" : "disabled"); +} + /** * pci_enable_wake - enable PCI device as wakeup event source * @dev: PCI device affected @@ -1046,66 +1108,83 @@ int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state) * called automatically by this routine. * * Devices with legacy power management (no standard PCI PM capabilities) - * always require such platform hooks. Depending on the platform, devices - * supporting the standard PCI PME# signal may require such platform hooks; - * they always update bits in config space to allow PME# generation. + * always require such platform hooks. * - * -EIO is returned if the device can't ever be a wakeup event source. - * -EINVAL is returned if the device can't generate wakeup events from - * the specified PCI state. Returns zero if the operation is successful. + * RETURN VALUE: + * 0 is returned on success + * -EINVAL is returned if device is not supposed to wake up the system + * Error code depending on the platform is returned if both the platform and + * the native mechanism fail to enable the generation of wake-up events */ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) { int pm; - int status; - u16 value; - - /* Note that drivers should verify device_may_wakeup(&dev->dev) - * before calling this function. Platform code should report - * errors when drivers try to enable wakeup on devices that - * can't issue wakeups, or on which wakeups were disabled by - * userspace updating the /sys/devices.../power/wakeup file. - */ + int error = 0; + bool pme_done = false; - status = call_platform_enable_wakeup(&dev->dev, enable); - - /* find PCI PM capability in list */ - pm = pci_find_capability(dev, PCI_CAP_ID_PM); + if (!device_may_wakeup(&dev->dev)) + return -EINVAL; - /* If device doesn't support PM Capabilities, but caller wants to - * disable wake events, it's a NOP. Otherwise fail unless the - * platform hooks handled this legacy device already. + /* + * According to "PCI System Architecture" 4th ed. by Tom Shanley & Don + * Anderson we should be doing PME# wake enable followed by ACPI wake + * enable. To disable wake-up we call the platform first, for symmetry. */ - if (!pm) - return enable ? status : 0; - /* Check device's ability to generate PME# */ - pci_read_config_word(dev,pm+PCI_PM_PMC,&value); + if (!enable && platform_pci_can_wakeup(dev)) + error = platform_pci_sleep_wake(dev, false); - value &= PCI_PM_CAP_PME_MASK; - value >>= ffs(PCI_PM_CAP_PME_MASK) - 1; /* First bit of mask */ - - /* Check if it can generate PME# from requested state. */ - if (!value || !(value & (1 << state))) { - /* if it can't, revert what the platform hook changed, - * always reporting the base "EINVAL, can't PME#" error - */ - if (enable) - call_platform_enable_wakeup(&dev->dev, 0); - return enable ? -EINVAL : 0; + pm = pci_find_capability(dev, PCI_CAP_ID_PM); + if (!enable || pci_pme_capable(dev, pm, state)) { + pci_pme_active(dev, pm, enable); + pme_done = true; } - pci_read_config_word(dev, pm + PCI_PM_CTRL, &value); + if (enable && platform_pci_can_wakeup(dev)) + error = platform_pci_sleep_wake(dev, true); - /* Clear PME_Status by writing 1 to it and enable PME# */ - value |= PCI_PM_CTRL_PME_STATUS | PCI_PM_CTRL_PME_ENABLE; + return pme_done ? 0 : error; +} - if (!enable) - value &= ~PCI_PM_CTRL_PME_ENABLE; +/** + * pci_pm_init - Initialize PM functions of given PCI device + * @dev: PCI device to handle. + */ +void pci_pm_init(struct pci_dev *dev) +{ + int pm; + u16 pmc; - pci_write_config_word(dev, pm + PCI_PM_CTRL, value); + /* find PCI PM capability in list */ + pm = pci_find_capability(dev, PCI_CAP_ID_PM); + if (!pm) + return; + /* Check device's ability to generate PME# */ + pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc); - return 0; + if ((pmc & PCI_PM_CAP_VER_MASK) > 3) { + dev_err(&dev->dev, "unsupported PM cap regs version (%u)\n", + pmc & PCI_PM_CAP_VER_MASK); + return; + } + + if (pmc & PCI_PM_CAP_PME_MASK) { + dev_printk(KERN_INFO, &dev->dev, + "PME# supported from%s%s%s%s%s\n", + (pmc & PCI_PM_CAP_PME_D0) ? " D0" : "", + (pmc & PCI_PM_CAP_PME_D1) ? " D1" : "", + (pmc & PCI_PM_CAP_PME_D2) ? " D2" : "", + (pmc & PCI_PM_CAP_PME_D3) ? " D3hot" : "", + (pmc & PCI_PM_CAP_PME_D3cold) ? " D3cold" : ""); + /* + * Make device's PM flags reflect the wake-up capability, but + * let the user space enable it to wake up the system as needed. + */ + device_set_wakeup_capable(&dev->dev, true); + device_set_wakeup_enable(&dev->dev, false); + /* Disable the PME# generation functionality */ + pci_pme_active(dev, pm, false); + } } int diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 0cd2e719933b..b08dfc9746af 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -17,6 +17,11 @@ extern void pci_cleanup_rom(struct pci_dev *dev); * platform; to be used during system-wide transitions from a * sleeping state to the working state and vice versa * + * @can_wakeup - returns 'true' if given device is capable of waking up the + * system from a sleeping state + * + * @sleep_wake - enables/disables the system wake up capability of given device + * * If given platform is generally capable of power managing PCI devices, all of * these callbacks are mandatory. */ @@ -24,9 +29,12 @@ struct pci_platform_pm_ops { bool (*is_manageable)(struct pci_dev *dev); int (*set_state)(struct pci_dev *dev, pci_power_t state); pci_power_t (*choose_state)(struct pci_dev *dev); + bool (*can_wakeup)(struct pci_dev *dev); + int (*sleep_wake)(struct pci_dev *dev, bool enable); }; extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops); +extern void pci_pm_init(struct pci_dev *dev); extern int pci_user_read_config_byte(struct pci_dev *dev, int where, u8 *val); extern int pci_user_read_config_word(struct pci_dev *dev, int where, u16 *val); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 2f0ae70710d6..b1724cf31b66 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -860,49 +860,6 @@ int pci_cfg_space_size_ext(struct pci_dev *dev) return PCI_CFG_SPACE_SIZE; } -/** - * pci_disable_pme - Disable the PME function of PCI device - * @dev: PCI device affected - * -EINVAL is returned if PCI device doesn't support PME. - * Zero is returned if the PME is supported and can be disabled. - */ -static int pci_disable_pme(struct pci_dev *dev) -{ - int pm; - u16 value; - - /* find PCI PM capability in list */ - pm = pci_find_capability(dev, PCI_CAP_ID_PM); - - /* If device doesn't support PM Capabilities, it means that PME is - * not supported. - */ - if (!pm) - return -EINVAL; - /* Check device's ability to generate PME# */ - pci_read_config_word(dev, pm + PCI_PM_PMC, &value); - - value &= PCI_PM_CAP_PME_MASK; - /* Check if it can generate PME# */ - if (!value) { - /* - * If it is zero, it means that PME is still unsupported - * although there exists the PM capability. - */ - return -EINVAL; - } - - pci_read_config_word(dev, pm + PCI_PM_CTRL, &value); - - /* Clear PME_Status by writing 1 to it */ - value |= PCI_PM_CTRL_PME_STATUS ; - /* Disable PME enable bit */ - value &= ~PCI_PM_CTRL_PME_ENABLE; - pci_write_config_word(dev, pm + PCI_PM_CTRL, value); - - return 0; -} - int pci_cfg_space_size(struct pci_dev *dev) { int pos; @@ -1010,7 +967,6 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn) } pci_vpd_pci22_init(dev); - pci_disable_pme(dev); return dev; } @@ -1031,6 +987,9 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) /* Fix up broken headers */ pci_fixup_device(pci_fixup_header, dev); + /* Initialize power management of the device */ + pci_pm_init(dev); + /* * Add the device to our list of discovered devices * and the bus list for fixup functions, etc. diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 071daf8db600..7ab5a611e43f 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -337,6 +337,7 @@ int acpi_bus_get_status(struct acpi_device *device); int acpi_bus_get_power(acpi_handle handle, int *state); int acpi_bus_set_power(acpi_handle handle, int state); bool acpi_bus_power_manageable(acpi_handle handle); +bool acpi_bus_can_wakeup(acpi_handle handle); #ifdef CONFIG_ACPI_PROC_EVENT int acpi_bus_generate_proc_event(struct acpi_device *device, u8 type, int data); int acpi_bus_generate_proc_event4(const char *class, const char *bid, u8 type, int data); @@ -379,6 +380,7 @@ acpi_handle acpi_get_pci_rootbridge_handle(unsigned int, unsigned int); #ifdef CONFIG_PM_SLEEP int acpi_pm_device_sleep_state(struct device *, int *); +int acpi_pm_device_sleep_wake(struct device *, bool); #else /* !CONFIG_PM_SLEEP */ static inline int acpi_pm_device_sleep_state(struct device *d, int *p) { diff --git a/include/linux/pm_wakeup.h b/include/linux/pm_wakeup.h index f0d0b2cb8d20..3af0c8d05cdc 100644 --- a/include/linux/pm_wakeup.h +++ b/include/linux/pm_wakeup.h @@ -35,6 +35,11 @@ static inline void device_init_wakeup(struct device *dev, int val) dev->power.can_wakeup = dev->power.should_wakeup = !!val; } +static inline void device_set_wakeup_capable(struct device *dev, int val) +{ + dev->power.can_wakeup = !!val; +} + static inline int device_can_wakeup(struct device *dev) { return dev->power.can_wakeup; @@ -47,21 +52,7 @@ static inline void device_set_wakeup_enable(struct device *dev, int val) static inline int device_may_wakeup(struct device *dev) { - return dev->power.can_wakeup & dev->power.should_wakeup; -} - -/* - * Platform hook to activate device wakeup capability, if that's not already - * handled by enable_irq_wake() etc. - * Returns zero on success, else negative errno - */ -extern int (*platform_enable_wakeup)(struct device *dev, int is_on); - -static inline int call_platform_enable_wakeup(struct device *dev, int is_on) -{ - if (platform_enable_wakeup) - return (*platform_enable_wakeup)(dev, is_on); - return 0; + return dev->power.can_wakeup && dev->power.should_wakeup; } #else /* !CONFIG_PM */ @@ -80,11 +71,6 @@ static inline int device_can_wakeup(struct device *dev) #define device_set_wakeup_enable(dev, val) do {} while (0) #define device_may_wakeup(dev) 0 -static inline int call_platform_enable_wakeup(struct device *dev, int is_on) -{ - return 0; -} - #endif /* !CONFIG_PM */ #endif /* _LINUX_PM_WAKEUP_H */ -- cgit v1.2.3 From 404cc2d8ce41ed4031958fba8e633767e8a2e028 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 7 Jul 2008 03:35:26 +0200 Subject: PCI PM: Introduce pci_prepare_to_sleep and pci_back_from_sleep Introduce functions pci_prepare_to_sleep() and pci_back_from_sleep(), to be used by the PCI drivers that want to place their devices into the lowest power state appropiate for them (PCI_D3hot, if the device is not supposed to wake up the system, or the deepest state from which the wake-up is possible, otherwise) while the system is being prepared to go into a sleeping state and to put them back into D0 during the subsequent transition to the working state. Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 2 ++ 2 files changed, 85 insertions(+) (limited to 'include/linux') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index a6b1b6f96abc..1b0ec45e9934 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1146,6 +1146,87 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) return pme_done ? 0 : error; } +/** + * pci_prepare_to_sleep - prepare PCI device for system-wide transition into + * a sleep state + * @dev: Device to handle. + * + * Choose the power state appropriate for the device depending on whether + * it can wake up the system and/or is power manageable by the platform + * (PCI_D3hot is the default) and put the device into that state. + */ +int pci_prepare_to_sleep(struct pci_dev *dev) +{ + pci_power_t target_state = PCI_D3hot; + int pm = pci_find_capability(dev, PCI_CAP_ID_PM); + int error; + + if (platform_pci_power_manageable(dev)) { + /* + * Call the platform to choose the target state of the device + * and enable wake-up from this state if supported. + */ + pci_power_t state = platform_pci_choose_state(dev); + + switch (state) { + case PCI_POWER_ERROR: + case PCI_UNKNOWN: + break; + case PCI_D1: + case PCI_D2: + if (pci_no_d1d2(dev)) + break; + default: + target_state = state; + pci_enable_wake(dev, target_state, true); + } + } else if (device_may_wakeup(&dev->dev)) { + /* + * Find the deepest state from which the device can generate + * wake-up events, make it the target state and enable device + * to generate PME#. + */ + u16 pmc; + + if (!pm) + return -EIO; + + pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc); + if (pmc & PCI_PM_CAP_PME_MASK) { + if (!(pmc & PCI_PM_CAP_PME_D3)) { + /* Device cannot generate PME# from D3_hot */ + if (pmc & PCI_PM_CAP_PME_D2) + target_state = PCI_D2; + else if (pmc & PCI_PM_CAP_PME_D1) + target_state = PCI_D1; + else + target_state = PCI_D0; + } + pci_pme_active(dev, pm, true); + } + } + + error = pci_set_power_state(dev, target_state); + + if (error) + pci_enable_wake(dev, target_state, false); + + return error; +} + +/** + * pci_back_from_sleep - turn PCI device on during system-wide transition into + * the working state a sleep state + * @dev: Device to handle. + * + * Disable device's sytem wake-up capability and put it into D0. + */ +int pci_back_from_sleep(struct pci_dev *dev) +{ + pci_enable_wake(dev, PCI_D0, false); + return pci_set_power_state(dev, PCI_D0); +} + /** * pci_pm_init - Initialize PM functions of given PCI device * @dev: PCI device to handle. @@ -1853,5 +1934,7 @@ EXPORT_SYMBOL(pci_set_power_state); EXPORT_SYMBOL(pci_save_state); EXPORT_SYMBOL(pci_restore_state); EXPORT_SYMBOL(pci_enable_wake); +EXPORT_SYMBOL(pci_prepare_to_sleep); +EXPORT_SYMBOL(pci_back_from_sleep); EXPORT_SYMBOL_GPL(pci_set_pcie_reset_state); diff --git a/include/linux/pci.h b/include/linux/pci.h index 4c80dc3f2990..52ac06dcce98 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -632,6 +632,8 @@ int pci_restore_state(struct pci_dev *dev); int pci_set_power_state(struct pci_dev *dev, pci_power_t state); pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state); int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable); +int pci_prepare_to_sleep(struct pci_dev *dev); +int pci_back_from_sleep(struct pci_dev *dev); /* Functions for PCI Hotplug drivers to use */ int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap); -- cgit v1.2.3 From 337001b6c42938f49a880b1b8306c3ed771a7e61 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 7 Jul 2008 03:36:24 +0200 Subject: PCI: Simplify PCI device PM code If the offset of PCI device's PM capability in its configuration space, the mask of states that the device supports PME# from and the D1 and D2 support bits are cached in the corresponding struct pci_dev, the PCI device PM code can be simplified quite a bit. Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 118 +++++++++++++++++++++-------------------------- include/linux/pci.h | 8 +++- include/linux/pci_regs.h | 1 + 3 files changed, 60 insertions(+), 67 deletions(-) (limited to 'include/linux') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 1b0ec45e9934..e632a58ba5d0 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -419,7 +419,6 @@ static inline int platform_pci_sleep_wake(struct pci_dev *dev, bool enable) * pci_raw_set_power_state - Use PCI PM registers to set the power state of * given PCI device * @dev: PCI device to handle. - * @pm: PCI PM capability offset of the device. * @state: PCI power state (D0, D1, D2, D3hot) to put the device into. * * RETURN VALUE: @@ -430,12 +429,12 @@ static inline int platform_pci_sleep_wake(struct pci_dev *dev, bool enable) * 0 if device's power state has been successfully changed. */ static int -pci_raw_set_power_state(struct pci_dev *dev, int pm, pci_power_t state) +pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state) { - u16 pmcsr, pmc; + u16 pmcsr; bool need_restore = false; - if (!pm) + if (!dev->pm_cap) return -EIO; if (state < PCI_D0 || state > PCI_D3hot) @@ -455,20 +454,12 @@ pci_raw_set_power_state(struct pci_dev *dev, int pm, pci_power_t state) return -EINVAL; } - pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc); - - if ((pmc & PCI_PM_CAP_VER_MASK) > 3) { - dev_err(&dev->dev, "unsupported PM cap regs version (%u)\n", - pmc & PCI_PM_CAP_VER_MASK); - return -EIO; - } - /* check if this device supports the desired state */ - if ((state == PCI_D1 && !(pmc & PCI_PM_CAP_D1)) - || (state == PCI_D2 && !(pmc & PCI_PM_CAP_D2))) + if ((state == PCI_D1 && !dev->d1_support) + || (state == PCI_D2 && !dev->d2_support)) return -EIO; - pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr); + pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); /* If we're (effectively) in D3, force entire word to 0. * This doesn't affect PME_Status, disables PME_En, and @@ -492,7 +483,7 @@ pci_raw_set_power_state(struct pci_dev *dev, int pm, pci_power_t state) } /* enter specified state */ - pci_write_config_word(dev, pm + PCI_PM_CTRL, pmcsr); + pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr); /* Mandatory power management transition delays */ /* see PCI PM 1.1 5.6.1 table 18 */ @@ -528,14 +519,13 @@ pci_raw_set_power_state(struct pci_dev *dev, int pm, pci_power_t state) * pci_update_current_state - Read PCI power state of given device from its * PCI PM registers and cache it * @dev: PCI device to handle. - * @pm: PCI PM capability offset of the device. */ -static void pci_update_current_state(struct pci_dev *dev, int pm) +static void pci_update_current_state(struct pci_dev *dev) { - if (pm) { + if (dev->pm_cap) { u16 pmcsr; - pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr); + pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK); } } @@ -557,7 +547,7 @@ static void pci_update_current_state(struct pci_dev *dev, int pm) */ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) { - int pm, error; + int error; /* bound the state we're entering */ if (state > PCI_D3hot) @@ -572,9 +562,6 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) */ return 0; - /* Find PCI PM capability in the list */ - pm = pci_find_capability(dev, PCI_CAP_ID_PM); - if (state == PCI_D0 && platform_pci_power_manageable(dev)) { /* * Allow the platform to change the state, for example via ACPI @@ -582,16 +569,16 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) */ int ret = platform_pci_set_power_state(dev, PCI_D0); if (!ret) - pci_update_current_state(dev, pm); + pci_update_current_state(dev); } - error = pci_raw_set_power_state(dev, pm, state); + error = pci_raw_set_power_state(dev, state); if (state > PCI_D0 && platform_pci_power_manageable(dev)) { /* Allow the platform to finalize the transition */ int ret = platform_pci_set_power_state(dev, state); if (!ret) { - pci_update_current_state(dev, pm); + pci_update_current_state(dev); error = 0; } } @@ -1050,48 +1037,38 @@ int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state) /** * pci_pme_capable - check the capability of PCI device to generate PME# * @dev: PCI device to handle. - * @pm: PCI PM capability offset of the device. * @state: PCI state from which device will issue PME#. */ -static bool pci_pme_capable(struct pci_dev *dev, int pm, pci_power_t state) +static bool pci_pme_capable(struct pci_dev *dev, pci_power_t state) { - u16 pmc; - - if (!pm) + if (!dev->pm_cap) return false; - /* Check device's ability to generate PME# from given state */ - pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc); - - pmc &= PCI_PM_CAP_PME_MASK; - pmc >>= ffs(PCI_PM_CAP_PME_MASK) - 1; /* First bit of mask */ - - return !!(pmc & (1 << state)); + return !!(dev->pme_support & (1 << state)); } /** * pci_pme_active - enable or disable PCI device's PME# function * @dev: PCI device to handle. - * @pm: PCI PM capability offset of the device. * @enable: 'true' to enable PME# generation; 'false' to disable it. * * The caller must verify that the device is capable of generating PME# before * calling this function with @enable equal to 'true'. */ -static void pci_pme_active(struct pci_dev *dev, int pm, bool enable) +static void pci_pme_active(struct pci_dev *dev, bool enable) { u16 pmcsr; - if (!pm) + if (!dev->pm_cap) return; - pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr); + pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); /* Clear PME_Status by writing 1 to it and enable PME# */ pmcsr |= PCI_PM_CTRL_PME_STATUS | PCI_PM_CTRL_PME_ENABLE; if (!enable) pmcsr &= ~PCI_PM_CTRL_PME_ENABLE; - pci_write_config_word(dev, pm + PCI_PM_CTRL, pmcsr); + pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr); dev_printk(KERN_INFO, &dev->dev, "PME# %s\n", enable ? "enabled" : "disabled"); @@ -1118,7 +1095,6 @@ static void pci_pme_active(struct pci_dev *dev, int pm, bool enable) */ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) { - int pm; int error = 0; bool pme_done = false; @@ -1134,9 +1110,8 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) if (!enable && platform_pci_can_wakeup(dev)) error = platform_pci_sleep_wake(dev, false); - pm = pci_find_capability(dev, PCI_CAP_ID_PM); - if (!enable || pci_pme_capable(dev, pm, state)) { - pci_pme_active(dev, pm, enable); + if (!enable || pci_pme_capable(dev, state)) { + pci_pme_active(dev, enable); pme_done = true; } @@ -1158,7 +1133,6 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) int pci_prepare_to_sleep(struct pci_dev *dev) { pci_power_t target_state = PCI_D3hot; - int pm = pci_find_capability(dev, PCI_CAP_ID_PM); int error; if (platform_pci_power_manageable(dev)) { @@ -1186,23 +1160,14 @@ int pci_prepare_to_sleep(struct pci_dev *dev) * wake-up events, make it the target state and enable device * to generate PME#. */ - u16 pmc; - - if (!pm) + if (!dev->pm_cap) return -EIO; - pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc); - if (pmc & PCI_PM_CAP_PME_MASK) { - if (!(pmc & PCI_PM_CAP_PME_D3)) { - /* Device cannot generate PME# from D3_hot */ - if (pmc & PCI_PM_CAP_PME_D2) - target_state = PCI_D2; - else if (pmc & PCI_PM_CAP_PME_D1) - target_state = PCI_D1; - else - target_state = PCI_D0; - } - pci_pme_active(dev, pm, true); + if (dev->pme_support) { + while (target_state + && !(dev->pme_support & (1 << target_state))) + target_state--; + pci_pme_active(dev, true); } } @@ -1236,6 +1201,8 @@ void pci_pm_init(struct pci_dev *dev) int pm; u16 pmc; + dev->pm_cap = 0; + /* find PCI PM capability in list */ pm = pci_find_capability(dev, PCI_CAP_ID_PM); if (!pm) @@ -1249,7 +1216,23 @@ void pci_pm_init(struct pci_dev *dev) return; } - if (pmc & PCI_PM_CAP_PME_MASK) { + dev->pm_cap = pm; + + dev->d1_support = false; + dev->d2_support = false; + if (!pci_no_d1d2(dev)) { + if (pmc & PCI_PM_CAP_D1) { + dev_printk(KERN_DEBUG, &dev->dev, "supports D1\n"); + dev->d1_support = true; + } + if (pmc & PCI_PM_CAP_D2) { + dev_printk(KERN_DEBUG, &dev->dev, "supports D2\n"); + dev->d2_support = true; + } + } + + pmc &= PCI_PM_CAP_PME_MASK; + if (pmc) { dev_printk(KERN_INFO, &dev->dev, "PME# supported from%s%s%s%s%s\n", (pmc & PCI_PM_CAP_PME_D0) ? " D0" : "", @@ -1257,6 +1240,7 @@ void pci_pm_init(struct pci_dev *dev) (pmc & PCI_PM_CAP_PME_D2) ? " D2" : "", (pmc & PCI_PM_CAP_PME_D3) ? " D3hot" : "", (pmc & PCI_PM_CAP_PME_D3cold) ? " D3cold" : ""); + dev->pme_support = pmc >> PCI_PM_CAP_PME_SHIFT; /* * Make device's PM flags reflect the wake-up capability, but * let the user space enable it to wake up the system as needed. @@ -1264,7 +1248,9 @@ void pci_pm_init(struct pci_dev *dev) device_set_wakeup_capable(&dev->dev, true); device_set_wakeup_enable(&dev->dev, false); /* Disable the PME# generation functionality */ - pci_pme_active(dev, pm, false); + pci_pme_active(dev, false); + } else { + dev->pme_support = 0; } } diff --git a/include/linux/pci.h b/include/linux/pci.h index 52ac06dcce98..68a29f0f2748 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -177,6 +177,13 @@ struct pci_dev { pci_power_t current_state; /* Current operating state. In ACPI-speak, this is D0-D3, D0 being fully functional, and D3 being off. */ + int pm_cap; /* PM capability offset in the + configuration space */ + unsigned int pme_support:5; /* Bitmask of states from which PME# + can be generated */ + unsigned int d1_support:1; /* Low power state D1 is supported */ + unsigned int d2_support:1; /* Low power state D2 is supported */ + unsigned int no_d1d2:1; /* Only allow D0 and D3 */ #ifdef CONFIG_PCIEASPM struct pcie_link_state *link_state; /* ASPM link state. */ @@ -201,7 +208,6 @@ struct pci_dev { unsigned int is_added:1; unsigned int is_busmaster:1; /* device is busmaster */ unsigned int no_msi:1; /* device may not use msi */ - unsigned int no_d1d2:1; /* only allow d0 or d3 */ unsigned int block_ucfg_access:1; /* userspace config space access is blocked */ unsigned int broken_parity_status:1; /* Device generates false positive parity */ unsigned int msi_enabled:1; diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index c0c1223c9194..19958b929905 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -231,6 +231,7 @@ #define PCI_PM_CAP_PME_D2 0x2000 /* PME# from D2 */ #define PCI_PM_CAP_PME_D3 0x4000 /* PME# from D3 (hot) */ #define PCI_PM_CAP_PME_D3cold 0x8000 /* PME# from D3 (cold) */ +#define PCI_PM_CAP_PME_SHIFT 11 /* Start of the PME Mask in PMC */ #define PCI_PM_CTRL 4 /* PM control and status register */ #define PCI_PM_CTRL_STATE_MASK 0x0003 /* Current power state (D0 to D3) */ #define PCI_PM_CTRL_NO_SOFT_RESET 0x0004 /* No reset for D3hot->D0 */ -- cgit v1.2.3 From cdf060a5d3c2afc7998af94b26a6c5182419e071 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 8 Jul 2008 02:36:40 -0700 Subject: netfilter: cleanup netfilter_ipv6.h userspace header Kernel functions are not for userspace. Signed-off-by: Adrian Bunk Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netfilter_ipv6.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include/linux') diff --git a/include/linux/netfilter_ipv6.h b/include/linux/netfilter_ipv6.h index fd50988b83ec..d654873aa25a 100644 --- a/include/linux/netfilter_ipv6.h +++ b/include/linux/netfilter_ipv6.h @@ -70,6 +70,8 @@ enum nf_ip6_hook_priorities { NF_IP6_PRI_LAST = INT_MAX, }; +#ifdef __KERNEL__ + #ifdef CONFIG_NETFILTER extern int ip6_route_me_harder(struct sk_buff *skb); extern __sum16 nf_ip6_checksum(struct sk_buff *skb, unsigned int hook, @@ -82,4 +84,6 @@ static inline int ipv6_netfilter_init(void) { return 0; } static inline void ipv6_netfilter_fini(void) { return; } #endif /* CONFIG_NETFILTER */ +#endif /* __KERNEL__ */ + #endif /*__LINUX_IP6_NETFILTER_H*/ -- cgit v1.2.3 From b9c796783151678d08b1ec1ef410685b2515ba51 Mon Sep 17 00:00:00 2001 From: Joonwoo Park Date: Tue, 8 Jul 2008 02:37:31 -0700 Subject: textsearch: support for case insensitive searching The function textsearch_prepare has a new flag to support case insensitive searching. Signed-off-by: Joonwoo Park Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/textsearch.h | 10 +++++----- lib/textsearch.c | 14 ++++++++------ 2 files changed, 13 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/textsearch.h b/include/linux/textsearch.h index 6f371f24160b..6c34cf28b7aa 100644 --- a/include/linux/textsearch.h +++ b/include/linux/textsearch.h @@ -10,10 +10,8 @@ struct ts_config; -/** - * TS_AUTOLOAD - Automatically load textsearch modules when needed - */ -#define TS_AUTOLOAD 1 +#define TS_AUTOLOAD 1 /* Automatically load textsearch modules when needed */ +#define TS_IGNORECASE 2 /* Searches string case insensitively */ /** * struct ts_state - search state @@ -39,7 +37,7 @@ struct ts_state struct ts_ops { const char *name; - struct ts_config * (*init)(const void *, unsigned int, gfp_t); + struct ts_config * (*init)(const void *, unsigned int, gfp_t, int); unsigned int (*find)(struct ts_config *, struct ts_state *); void (*destroy)(struct ts_config *); @@ -52,12 +50,14 @@ struct ts_ops /** * struct ts_config - search configuration * @ops: operations of chosen algorithm + * @flags: flags * @get_next_block: callback to fetch the next block to search in * @finish: callback to finalize a search */ struct ts_config { struct ts_ops *ops; + int flags; /** * get_next_block - fetch next block of data diff --git a/lib/textsearch.c b/lib/textsearch.c index be8bda3862f5..b451fcc9354c 100644 --- a/lib/textsearch.c +++ b/lib/textsearch.c @@ -54,10 +54,13 @@ * USAGE * * Before a search can be performed, a configuration must be created - * by calling textsearch_prepare() specyfing the searching algorithm and - * the pattern to look for. The returned configuration may then be used - * for an arbitary amount of times and even in parallel as long as a - * separate struct ts_state variable is provided to every instance. + * by calling textsearch_prepare() specifying the searching algorithm, + * the pattern to look for and flags. As a flag, you can set TS_IGNORECASE + * to perform case insensitive matching. But it might slow down + * performance of algorithm, so you should use it at own your risk. + * The returned configuration may then be used for an arbitary + * amount of times and even in parallel as long as a separate struct + * ts_state variable is provided to every instance. * * The actual search is performed by either calling textsearch_find_- * continuous() for linear data or by providing an own get_next_block() @@ -89,7 +92,6 @@ * panic("Oh my god, dancing chickens at %d\n", pos); * * textsearch_destroy(conf); - * * ========================================================================== */ @@ -279,7 +281,7 @@ struct ts_config *textsearch_prepare(const char *algo, const void *pattern, if (ops == NULL) goto errout; - conf = ops->init(pattern, len, gfp_mask); + conf = ops->init(pattern, len, gfp_mask, flags); if (IS_ERR(conf)) { err = PTR_ERR(conf); goto errout; -- cgit v1.2.3 From dde77e604497dada6f224a685278dfb27747ae52 Mon Sep 17 00:00:00 2001 From: Joonwoo Park Date: Tue, 8 Jul 2008 02:38:40 -0700 Subject: textsearch: convert kmalloc + memset to kzalloc convert kmalloc + memset to kzalloc for alloc_ts_config Signed-off-by: Joonwoo Park Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/textsearch.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/textsearch.h b/include/linux/textsearch.h index 6c34cf28b7aa..d9a85d616385 100644 --- a/include/linux/textsearch.h +++ b/include/linux/textsearch.h @@ -162,11 +162,10 @@ static inline struct ts_config *alloc_ts_config(size_t payload, { struct ts_config *conf; - conf = kmalloc(TS_PRIV_ALIGN(sizeof(*conf)) + payload, gfp_mask); + conf = kzalloc(TS_PRIV_ALIGN(sizeof(*conf)) + payload, gfp_mask); if (conf == NULL) return ERR_PTR(-ENOMEM); - memset(conf, 0, TS_PRIV_ALIGN(sizeof(*conf)) + payload); return conf; } -- cgit v1.2.3 From 4ad3f26162ece5aca3045fd45e15dd99acea4a0e Mon Sep 17 00:00:00 2001 From: Joonwoo Park Date: Tue, 8 Jul 2008 02:38:56 -0700 Subject: netfilter: fix string extension for case insensitive pattern matching The flag XT_STRING_FLAG_IGNORECASE indicates case insensitive string matching. netfilter can find cmd.exe, Cmd.exe, cMd.exe and etc easily. A new revision 1 was added, in the meantime invert of xt_string_info was moved into flags as a flag. If revision is 1, The flag XT_STRING_FLAG_INVERT indicates invert matching. Signed-off-by: Joonwoo Park Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netfilter/xt_string.h | 15 ++++++++++++++- net/netfilter/xt_string.c | 38 +++++++++++++++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/xt_string.h b/include/linux/netfilter/xt_string.h index bb21dd1aee2d..8a6ba7bbef9f 100644 --- a/include/linux/netfilter/xt_string.h +++ b/include/linux/netfilter/xt_string.h @@ -4,6 +4,11 @@ #define XT_STRING_MAX_PATTERN_SIZE 128 #define XT_STRING_MAX_ALGO_NAME_SIZE 16 +enum { + XT_STRING_FLAG_INVERT = 0x01, + XT_STRING_FLAG_IGNORECASE = 0x02 +}; + struct xt_string_info { u_int16_t from_offset; @@ -11,7 +16,15 @@ struct xt_string_info char algo[XT_STRING_MAX_ALGO_NAME_SIZE]; char pattern[XT_STRING_MAX_PATTERN_SIZE]; u_int8_t patlen; - u_int8_t invert; + union { + struct { + u_int8_t invert; + } v0; + + struct { + u_int8_t flags; + } v1; + } u; /* Used internally by the kernel */ struct ts_config __attribute__((aligned(8))) *config; diff --git a/net/netfilter/xt_string.c b/net/netfilter/xt_string.c index 72f694d947f4..4903182a062b 100644 --- a/net/netfilter/xt_string.c +++ b/net/netfilter/xt_string.c @@ -29,12 +29,16 @@ string_mt(const struct sk_buff *skb, const struct net_device *in, { const struct xt_string_info *conf = matchinfo; struct ts_state state; + int invert; memset(&state, 0, sizeof(struct ts_state)); + invert = (match->revision == 0 ? conf->u.v0.invert : + conf->u.v1.flags & XT_STRING_FLAG_INVERT); + return (skb_find_text((struct sk_buff *)skb, conf->from_offset, conf->to_offset, conf->config, &state) - != UINT_MAX) ^ conf->invert; + != UINT_MAX) ^ invert; } #define STRING_TEXT_PRIV(m) ((struct xt_string_info *)(m)) @@ -46,6 +50,7 @@ string_mt_check(const char *tablename, const void *ip, { struct xt_string_info *conf = matchinfo; struct ts_config *ts_conf; + int flags = TS_AUTOLOAD; /* Damn, can't handle this case properly with iptables... */ if (conf->from_offset > conf->to_offset) @@ -54,8 +59,15 @@ string_mt_check(const char *tablename, const void *ip, return false; if (conf->patlen > XT_STRING_MAX_PATTERN_SIZE) return false; + if (match->revision == 1) { + if (conf->u.v1.flags & + ~(XT_STRING_FLAG_IGNORECASE | XT_STRING_FLAG_INVERT)) + return false; + if (conf->u.v1.flags & XT_STRING_FLAG_IGNORECASE) + flags |= TS_IGNORECASE; + } ts_conf = textsearch_prepare(conf->algo, conf->pattern, conf->patlen, - GFP_KERNEL, TS_AUTOLOAD); + GFP_KERNEL, flags); if (IS_ERR(ts_conf)) return false; @@ -72,6 +84,17 @@ static void string_mt_destroy(const struct xt_match *match, void *matchinfo) static struct xt_match string_mt_reg[] __read_mostly = { { .name = "string", + .revision = 0, + .family = AF_INET, + .checkentry = string_mt_check, + .match = string_mt, + .destroy = string_mt_destroy, + .matchsize = sizeof(struct xt_string_info), + .me = THIS_MODULE + }, + { + .name = "string", + .revision = 1, .family = AF_INET, .checkentry = string_mt_check, .match = string_mt, @@ -81,6 +104,17 @@ static struct xt_match string_mt_reg[] __read_mostly = { }, { .name = "string", + .revision = 0, + .family = AF_INET6, + .checkentry = string_mt_check, + .match = string_mt, + .destroy = string_mt_destroy, + .matchsize = sizeof(struct xt_string_info), + .me = THIS_MODULE + }, + { + .name = "string", + .revision = 1, .family = AF_INET6, .checkentry = string_mt_check, .match = string_mt, -- cgit v1.2.3 From 2c693610fe923764fe41b846fb86938a2010da6e Mon Sep 17 00:00:00 2001 From: Richard Kennedy Date: Tue, 8 Jul 2008 03:03:01 -0700 Subject: net: remove padding from struct socket on 64bit & increase objects/cache remove padding from struct socket reducing its size by 8 bytes. This allows more objects/cache in sock_inode_cache 12 objects/cache when cacheline size is 128 (generic x86_64) Signed-off-by: Richard Kennedy Signed-off-by: David S. Miller --- include/linux/net.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/net.h b/include/linux/net.h index 71f7dd559285..150a48c68d52 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -106,23 +106,23 @@ enum sock_shutdown_cmd { /** * struct socket - general BSD socket * @state: socket state (%SS_CONNECTED, etc) + * @type: socket type (%SOCK_STREAM, etc) * @flags: socket flags (%SOCK_ASYNC_NOSPACE, etc) * @ops: protocol specific socket operations * @fasync_list: Asynchronous wake up list * @file: File back pointer for gc * @sk: internal networking protocol agnostic socket representation * @wait: wait queue for several uses - * @type: socket type (%SOCK_STREAM, etc) */ struct socket { socket_state state; + short type; unsigned long flags; const struct proto_ops *ops; struct fasync_struct *fasync_list; struct file *file; struct sock *sk; wait_queue_head_t wait; - short type; }; struct vm_area_struct; -- cgit v1.2.3 From acc81e1465d29e0284008770cc4b8bc90bd93bd7 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 8 Jul 2008 03:21:27 -0700 Subject: vlan: fix network_header/mac_header adjustments Lennert Buytenhek points out that the VLAN code incorrectly adjusts skb->network_header to point in the middle of the VLAN header and additionally tries to adjust skb->mac_header without checking for validity. The network_header should not be touched at all since we're only adding headers in front of it, mac_header adjustments are not necessary at all. Based on patch by Lennert Buytenhek . Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/if_vlan.h | 2 -- net/8021q/vlan_dev.c | 1 - 2 files changed, 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 5190452ac7dc..8f5bf9b676aa 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -279,8 +279,6 @@ static inline struct sk_buff *__vlan_put_tag(struct sk_buff *skb, unsigned short veth->h_vlan_TCI = htons(tag); skb->protocol = htons(ETH_P_8021Q); - skb->mac_header -= VLAN_HLEN; - skb->network_header -= VLAN_HLEN; return skb; } diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index a0617bf7cec6..4a8525927c27 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -308,7 +308,6 @@ static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev, vhdr->h_vlan_encapsulated_proto = htons(len); skb->protocol = htons(ETH_P_8021Q); - skb_reset_network_header(skb); } /* Before delegating work to the lower layer, enter our MAC-address */ -- cgit v1.2.3 From 7750f403cbe56971336d575b354365190b4e3227 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 8 Jul 2008 03:23:36 -0700 Subject: vlan: uninline __vlan_hwaccel_rx The function is huge and included at least once in every VLAN acceleration capable driver. Uninline it; to avoid having drivers depend on the VLAN module, the function is always built in statically when VLAN is enabled. With all VLAN acceleration capable drivers that build on x86_64 enabled, this results in: text data bss dec hex filename 6515227 854044 343968 7713239 75b1d7 vmlinux.inlined 6505637 854044 343968 7703649 758c61 vmlinux.uninlined ---------------------------------------------------------- -9590 Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/if_vlan.h | 64 +++++++------------------------------------------ net/8021q/Makefile | 9 +++---- net/8021q/vlan.h | 8 +++++++ net/8021q/vlan_core.c | 48 +++++++++++++++++++++++++++++++++++++ net/Makefile | 4 +++- 5 files changed, 72 insertions(+), 61 deletions(-) create mode 100644 net/8021q/vlan_core.c (limited to 'include/linux') diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 8f5bf9b676aa..594cd35b0074 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -150,15 +150,6 @@ static inline struct vlan_dev_info *vlan_dev_info(const struct net_device *dev) return netdev_priv(dev); } -/* inline functions */ -static inline __u32 vlan_get_ingress_priority(struct net_device *dev, - unsigned short vlan_tag) -{ - struct vlan_dev_info *vip = vlan_dev_info(dev); - - return vip->ingress_priority_map[(vlan_tag >> 13) & 0x7]; -} - /* VLAN tx hw acceleration helpers. */ struct vlan_skb_tx_cookie { u32 magic; @@ -171,56 +162,17 @@ struct vlan_skb_tx_cookie { (VLAN_TX_SKB_CB(__skb)->magic == VLAN_TX_COOKIE_MAGIC) #define vlan_tx_tag_get(__skb) (VLAN_TX_SKB_CB(__skb)->vlan_tag) -/* VLAN rx hw acceleration helper. This acts like netif_{rx,receive_skb}(). */ -static inline int __vlan_hwaccel_rx(struct sk_buff *skb, - struct vlan_group *grp, +#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) +extern int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, + unsigned short vlan_tag, int polling); +#else +static inline int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, unsigned short vlan_tag, int polling) { - struct net_device_stats *stats; - - if (skb_bond_should_drop(skb)) { - dev_kfree_skb_any(skb); - return NET_RX_DROP; - } - - skb->dev = vlan_group_get_device(grp, vlan_tag & VLAN_VID_MASK); - if (skb->dev == NULL) { - dev_kfree_skb_any(skb); - - /* Not NET_RX_DROP, this is not being dropped - * due to congestion. - */ - return 0; - } - - skb->dev->last_rx = jiffies; - - stats = &skb->dev->stats; - stats->rx_packets++; - stats->rx_bytes += skb->len; - - skb->priority = vlan_get_ingress_priority(skb->dev, vlan_tag); - switch (skb->pkt_type) { - case PACKET_BROADCAST: - break; - - case PACKET_MULTICAST: - stats->multicast++; - break; - - case PACKET_OTHERHOST: - /* Our lower layer thinks this is not local, let's make sure. - * This allows the VLAN to have a different MAC than the underlying - * device, and still route correctly. - */ - if (!compare_ether_addr(eth_hdr(skb)->h_dest, - skb->dev->dev_addr)) - skb->pkt_type = PACKET_HOST; - break; - }; - - return (polling ? netif_receive_skb(skb) : netif_rx(skb)); + BUG(); + return NET_XMIT_SUCCESS; } +#endif static inline int vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, diff --git a/net/8021q/Makefile b/net/8021q/Makefile index 3006e9ed7b08..9f4f174ead1c 100644 --- a/net/8021q/Makefile +++ b/net/8021q/Makefile @@ -1,9 +1,10 @@ # # Makefile for the Linux VLAN layer. # +obj-$(subst m,y,$(CONFIG_VLAN_8021Q)) += vlan_core.o +obj-$(CONFIG_VLAN_8021Q) += 8021q.o -obj-$(CONFIG_VLAN_8021Q) += 8021q.o +8021q-y := vlan.o vlan_dev.o vlan_netlink.o +8021q-$(CONFIG_VLAN_8021Q_GVRP) += vlan_gvrp.o +8021q-$(CONFIG_PROC_FS) += vlanproc.o -8021q-y := vlan.o vlan_dev.o vlan_netlink.o -8021q-$(CONFIG_VLAN_8021Q_GVRP) += vlan_gvrp.o -8021q-$(CONFIG_PROC_FS) += vlanproc.o diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h index 097b2e04c928..7cc1a97c42fc 100644 --- a/net/8021q/vlan.h +++ b/net/8021q/vlan.h @@ -37,6 +37,14 @@ void vlan_setup(struct net_device *dev); int register_vlan_dev(struct net_device *dev); void unregister_vlan_dev(struct net_device *dev); +static inline u32 vlan_get_ingress_priority(struct net_device *dev, + unsigned short vlan_tag) +{ + struct vlan_dev_info *vip = vlan_dev_info(dev); + + return vip->ingress_priority_map[(vlan_tag >> 13) & 0x7]; +} + #ifdef CONFIG_VLAN_8021Q_GVRP extern int vlan_gvrp_request_join(const struct net_device *dev); extern void vlan_gvrp_request_leave(const struct net_device *dev); diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c new file mode 100644 index 000000000000..85c94edb000f --- /dev/null +++ b/net/8021q/vlan_core.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include "vlan.h" + +/* VLAN rx hw acceleration helper. This acts like netif_{rx,receive_skb}(). */ +int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, + unsigned short vlan_tag, int polling) +{ + struct net_device_stats *stats; + + if (skb_bond_should_drop(skb)) { + dev_kfree_skb_any(skb); + return NET_RX_DROP; + } + + skb->dev = vlan_group_get_device(grp, vlan_tag & VLAN_VID_MASK); + if (skb->dev == NULL) { + dev_kfree_skb_any(skb); + /* Not NET_RX_DROP, this is not being dropped + * due to congestion. */ + return NET_RX_SUCCESS; + } + skb->dev->last_rx = jiffies; + + stats = &skb->dev->stats; + stats->rx_packets++; + stats->rx_bytes += skb->len; + + skb->priority = vlan_get_ingress_priority(skb->dev, vlan_tag); + switch (skb->pkt_type) { + case PACKET_BROADCAST: + break; + case PACKET_MULTICAST: + stats->multicast++; + break; + case PACKET_OTHERHOST: + /* Our lower layer thinks this is not local, let's make sure. + * This allows the VLAN to have a different MAC than the + * underlying device, and still route correctly. */ + if (!compare_ether_addr(eth_hdr(skb)->h_dest, + skb->dev->dev_addr)) + skb->pkt_type = PACKET_HOST; + break; + }; + return (polling ? netif_receive_skb(skb) : netif_rx(skb)); +} +EXPORT_SYMBOL(__vlan_hwaccel_rx); diff --git a/net/Makefile b/net/Makefile index b7a13643b549..4f43e7f874f3 100644 --- a/net/Makefile +++ b/net/Makefile @@ -42,7 +42,9 @@ obj-$(CONFIG_AF_RXRPC) += rxrpc/ obj-$(CONFIG_ATM) += atm/ obj-$(CONFIG_DECNET) += decnet/ obj-$(CONFIG_ECONET) += econet/ -obj-$(CONFIG_VLAN_8021Q) += 8021q/ +ifneq ($(CONFIG_VLAN_8021Q),) +obj-y += 8021q/ +endif obj-$(CONFIG_IP_DCCP) += dccp/ obj-$(CONFIG_IP_SCTP) += sctp/ obj-y += wireless/ -- cgit v1.2.3 From 22d1ba74bbafa96d3f425cc12714d3fe8675183f Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 8 Jul 2008 03:23:57 -0700 Subject: vlan: move struct vlan_dev_info to private header Hide struct vlan_dev_info from drivers to prevent them from growing more creative ways to use it. Provide accessors for the two drivers that currently use it. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- drivers/net/cxgb3/l2t.c | 2 +- drivers/s390/net/qeth_l3_main.c | 4 +-- include/linux/if_vlan.h | 56 +++++++++++------------------------------ net/8021q/vlan.c | 4 +-- net/8021q/vlan.h | 50 +++++++++++++++++++++++++++++++++++- net/8021q/vlan_core.c | 12 +++++++++ net/8021q/vlan_dev.c | 5 ---- 7 files changed, 80 insertions(+), 53 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/cxgb3/l2t.c b/drivers/net/cxgb3/l2t.c index f510140885ae..825e510bd9ed 100644 --- a/drivers/net/cxgb3/l2t.c +++ b/drivers/net/cxgb3/l2t.c @@ -337,7 +337,7 @@ struct l2t_entry *t3_l2t_get(struct t3cdev *cdev, struct neighbour *neigh, atomic_set(&e->refcnt, 1); neigh_replace(e, neigh); if (neigh->dev->priv_flags & IFF_802_1Q_VLAN) - e->vlan = vlan_dev_info(neigh->dev)->vlan_id; + e->vlan = vlan_dev_vlan_id(neigh->dev); else e->vlan = VLAN_NONE; spin_unlock(&e->lock); diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 999552c83bbe..85be40abdda9 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -2020,7 +2020,7 @@ static int qeth_l3_verify_vlan_dev(struct net_device *dev, } } - if (rc && !(netdev_priv(vlan_dev_info(dev)->real_dev) == (void *)card)) + if (rc && !(netdev_priv(vlan_dev_real_dev(dev)) == (void *)card)) return 0; return rc; @@ -2056,7 +2056,7 @@ static struct qeth_card *qeth_l3_get_card_from_dev(struct net_device *dev) if (rc == QETH_REAL_CARD) card = netdev_priv(dev); else if (rc == QETH_VLAN_CARD) - card = netdev_priv(vlan_dev_info(dev)->real_dev); + card = netdev_priv(vlan_dev_real_dev(dev)); if (card && card->options.layer2) card = NULL; QETH_DBF_TEXT_(TRACE, 4, "%d", rc); diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 594cd35b0074..cb2e6b480882 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -109,47 +109,6 @@ static inline void vlan_group_set_device(struct vlan_group *vg, array[vlan_id % VLAN_GROUP_ARRAY_PART_LEN] = dev; } -struct vlan_priority_tci_mapping { - u32 priority; - unsigned short vlan_qos; /* This should be shifted when first set, so we only do it - * at provisioning time. - * ((skb->priority << 13) & 0xE000) - */ - struct vlan_priority_tci_mapping *next; -}; - -/* Holds information that makes sense if this device is a VLAN device. */ -struct vlan_dev_info { - /** This will be the mapping that correlates skb->priority to - * 3 bits of VLAN QOS tags... - */ - unsigned int nr_ingress_mappings; - u32 ingress_priority_map[8]; - - unsigned int nr_egress_mappings; - struct vlan_priority_tci_mapping *egress_priority_map[16]; /* hash table */ - - unsigned short vlan_id; /* The VLAN Identifier for this interface. */ - unsigned short flags; /* (1 << 0) re_order_header This option will cause the - * VLAN code to move around the ethernet header on - * ingress to make the skb look **exactly** like it - * came in from an ethernet port. This destroys some of - * the VLAN information in the skb, but it fixes programs - * like DHCP that use packet-filtering and don't understand - * 802.1Q - */ - struct net_device *real_dev; /* the underlying device/interface */ - unsigned char real_dev_addr[ETH_ALEN]; - struct proc_dir_entry *dent; /* Holds the proc data */ - unsigned long cnt_inc_headroom_on_tx; /* How many times did we have to grow the skb on TX. */ - unsigned long cnt_encap_on_xmit; /* How many times did we have to encapsulate the skb on TX. */ -}; - -static inline struct vlan_dev_info *vlan_dev_info(const struct net_device *dev) -{ - return netdev_priv(dev); -} - /* VLAN tx hw acceleration helpers. */ struct vlan_skb_tx_cookie { u32 magic; @@ -163,9 +122,24 @@ struct vlan_skb_tx_cookie { #define vlan_tx_tag_get(__skb) (VLAN_TX_SKB_CB(__skb)->vlan_tag) #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) +extern struct net_device *vlan_dev_real_dev(const struct net_device *dev); +extern u16 vlan_dev_vlan_id(const struct net_device *dev); + extern int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, unsigned short vlan_tag, int polling); #else +static inline struct net_device *vlan_dev_real_dev(const struct net_device *dev) +{ + BUG(); + return NULL; +} + +static inline u16 vlan_dev_vlan_id(const struct net_device *dev) +{ + BUG(); + return 0; +} + static inline int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, unsigned short vlan_tag, int polling) { diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 8141e2dc510b..7a2625d2f9a0 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -543,7 +543,6 @@ static struct notifier_block vlan_notifier_block __read_mostly = { static int vlan_ioctl_handler(struct net *net, void __user *arg) { int err; - unsigned short vid = 0; struct vlan_ioctl_args args; struct net_device *dev = NULL; @@ -644,8 +643,7 @@ static int vlan_ioctl_handler(struct net *net, void __user *arg) case GET_VLAN_VID_CMD: err = 0; - vlan_dev_get_vid(dev, &vid); - args.u.VID = vid; + args.u.VID = vlan_dev_vlan_id(dev); if (copy_to_user(arg, &args, sizeof(struct vlan_ioctl_args))) err = -EFAULT; diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h index 7cc1a97c42fc..14c421e033f2 100644 --- a/net/8021q/vlan.h +++ b/net/8021q/vlan.h @@ -3,6 +3,55 @@ #include + +/** + * struct vlan_priority_tci_mapping - vlan egress priority mappings + * @priority: skb priority + * @vlan_qos: vlan priority: (skb->priority << 13) & 0xE000 + * @next: pointer to next struct + */ +struct vlan_priority_tci_mapping { + u32 priority; + unsigned short vlan_qos; + struct vlan_priority_tci_mapping *next; +}; + +/** + * struct vlan_dev_info - VLAN private device data + * @nr_ingress_mappings: number of ingress priority mappings + * @ingress_priority_map: ingress priority mappings + * @nr_egress_mappings: number of egress priority mappings + * @egress_priority_map: hash of egress priority mappings + * @vlan_id: VLAN identifier + * @flags: device flags + * @real_dev: underlying netdevice + * @real_dev_addr: address of underlying netdevice + * @dent: proc dir entry + * @cnt_inc_headroom_on_tx: statistic - number of skb expansions on TX + * @cnt_encap_on_xmit: statistic - number of skb encapsulations on TX + */ +struct vlan_dev_info { + unsigned int nr_ingress_mappings; + u32 ingress_priority_map[8]; + unsigned int nr_egress_mappings; + struct vlan_priority_tci_mapping *egress_priority_map[16]; + + unsigned short vlan_id; + unsigned short flags; + + struct net_device *real_dev; + unsigned char real_dev_addr[ETH_ALEN]; + + struct proc_dir_entry *dent; + unsigned long cnt_inc_headroom_on_tx; + unsigned long cnt_encap_on_xmit; +}; + +static inline struct vlan_dev_info *vlan_dev_info(const struct net_device *dev) +{ + return netdev_priv(dev); +} + #define VLAN_GRP_HASH_SHIFT 5 #define VLAN_GRP_HASH_SIZE (1 << VLAN_GRP_HASH_SHIFT) #define VLAN_GRP_HASH_MASK (VLAN_GRP_HASH_SIZE - 1) @@ -30,7 +79,6 @@ int vlan_dev_set_egress_priority(const struct net_device *dev, u32 skb_prio, short vlan_prio); int vlan_dev_change_flags(const struct net_device *dev, u32 flag, u32 mask); void vlan_dev_get_realdev_name(const struct net_device *dev, char *result); -void vlan_dev_get_vid(const struct net_device *dev, unsigned short *result); int vlan_check_real_dev(struct net_device *real_dev, unsigned short vlan_id); void vlan_setup(struct net_device *dev); diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c index 85c94edb000f..f980b9154cc3 100644 --- a/net/8021q/vlan_core.c +++ b/net/8021q/vlan_core.c @@ -46,3 +46,15 @@ int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, return (polling ? netif_receive_skb(skb) : netif_rx(skb)); } EXPORT_SYMBOL(__vlan_hwaccel_rx); + +struct net_device *vlan_dev_real_dev(const struct net_device *dev) +{ + return vlan_dev_info(dev)->real_dev; +} +EXPORT_SYMBOL_GPL(vlan_dev_real_dev); + +u16 vlan_dev_vlan_id(const struct net_device *dev) +{ + return vlan_dev_info(dev)->vlan_id; +} +EXPORT_SYMBOL_GPL(vlan_dev_vlan_id); diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 722697d31e4f..2aab294c5744 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -531,11 +531,6 @@ void vlan_dev_get_realdev_name(const struct net_device *dev, char *result) strncpy(result, vlan_dev_info(dev)->real_dev->name, 23); } -void vlan_dev_get_vid(const struct net_device *dev, unsigned short *result) -{ - *result = vlan_dev_info(dev)->vlan_id; -} - static int vlan_dev_open(struct net_device *dev) { struct vlan_dev_info *vlan = vlan_dev_info(dev); -- cgit v1.2.3 From df6b6a0cf62afeacdeb4c1a35b8fba21fda54399 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 8 Jul 2008 03:24:14 -0700 Subject: vlan: remove useless struct hlist_node declaration from if_vlan.h Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/if_vlan.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index cb2e6b480882..8e68b05b13df 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -14,10 +14,6 @@ #define _LINUX_IF_VLAN_H_ #ifdef __KERNEL__ - -/* externally defined structs */ -struct hlist_node; - #include #include -- cgit v1.2.3 From 9bb8582efb555521c7eec595ebd34e835ddc34b8 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 8 Jul 2008 03:24:44 -0700 Subject: vlan: TCI related type and naming cleanups The VLAN code contains multiple spots that use tag, id and tci as identifiers for arguments and variables incorrectly and they actually contain or are expected to contain something different. Additionally types are used inconsistently (unsigned short vs u16) and identifiers are sometimes capitalized. - consistently use u16 for storing TCI, ID or QoS values - consistently use vlan_id and vlan_tci for storing the respective values - remove capitalization - add kdoc comment to netif_hwaccel_{rx,receive_skb} Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/if_vlan.h | 84 ++++++++++++++++++++++++++++--------------------- net/8021q/vlan.c | 34 ++++++++++---------- net/8021q/vlan.h | 19 ++++++----- net/8021q/vlan_core.c | 6 ++-- net/8021q/vlan_dev.c | 47 ++++++++++++++------------- net/8021q/vlan_gvrp.c | 8 ++--- 6 files changed, 103 insertions(+), 95 deletions(-) (limited to 'include/linux') diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 8e68b05b13df..d36515dae62f 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -87,7 +87,7 @@ struct vlan_group { }; static inline struct net_device *vlan_group_get_device(struct vlan_group *vg, - unsigned int vlan_id) + u16 vlan_id) { struct net_device **array; array = vg->vlan_devices_arrays[vlan_id / VLAN_GROUP_ARRAY_PART_LEN]; @@ -95,7 +95,7 @@ static inline struct net_device *vlan_group_get_device(struct vlan_group *vg, } static inline void vlan_group_set_device(struct vlan_group *vg, - unsigned int vlan_id, + u16 vlan_id, struct net_device *dev) { struct net_device **array; @@ -122,7 +122,7 @@ extern struct net_device *vlan_dev_real_dev(const struct net_device *dev); extern u16 vlan_dev_vlan_id(const struct net_device *dev); extern int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, - unsigned short vlan_tag, int polling); + u16 vlan_tci, int polling); #else static inline struct net_device *vlan_dev_real_dev(const struct net_device *dev) { @@ -137,39 +137,51 @@ static inline u16 vlan_dev_vlan_id(const struct net_device *dev) } static inline int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, - unsigned short vlan_tag, int polling) + u16 vlan_tci, int polling) { BUG(); return NET_XMIT_SUCCESS; } #endif +/** + * vlan_hwaccel_rx - netif_rx wrapper for VLAN RX acceleration + * @skb: buffer + * @grp: vlan group + * @vlan_tci: VLAN TCI as received from the card + */ static inline int vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, - unsigned short vlan_tag) + u16 vlan_tci) { - return __vlan_hwaccel_rx(skb, grp, vlan_tag, 0); + return __vlan_hwaccel_rx(skb, grp, vlan_tci, 0); } +/** + * vlan_hwaccel_receive_skb - netif_receive_skb wrapper for VLAN RX acceleration + * @skb: buffer + * @grp: vlan group + * @vlan_tci: VLAN TCI as received from the card + */ static inline int vlan_hwaccel_receive_skb(struct sk_buff *skb, struct vlan_group *grp, - unsigned short vlan_tag) + u16 vlan_tci) { - return __vlan_hwaccel_rx(skb, grp, vlan_tag, 1); + return __vlan_hwaccel_rx(skb, grp, vlan_tci, 1); } /** * __vlan_put_tag - regular VLAN tag inserting * @skb: skbuff to tag - * @tag: VLAN tag to insert + * @vlan_tci: VLAN TCI to insert * * Inserts the VLAN tag into @skb as part of the payload * Returns a VLAN tagged skb. If a new skb is created, @skb is freed. - * + * * Following the skb_unshare() example, in case of error, the calling function * doesn't have to worry about freeing the original skb. */ -static inline struct sk_buff *__vlan_put_tag(struct sk_buff *skb, unsigned short tag) +static inline struct sk_buff *__vlan_put_tag(struct sk_buff *skb, u16 vlan_tci) { struct vlan_ethhdr *veth; @@ -197,8 +209,8 @@ static inline struct sk_buff *__vlan_put_tag(struct sk_buff *skb, unsigned short /* first, the ethernet type */ veth->h_vlan_proto = htons(ETH_P_8021Q); - /* now, the tag */ - veth->h_vlan_TCI = htons(tag); + /* now, the TCI */ + veth->h_vlan_TCI = htons(vlan_tci); skb->protocol = htons(ETH_P_8021Q); @@ -208,17 +220,18 @@ static inline struct sk_buff *__vlan_put_tag(struct sk_buff *skb, unsigned short /** * __vlan_hwaccel_put_tag - hardware accelerated VLAN inserting * @skb: skbuff to tag - * @tag: VLAN tag to insert + * @vlan_tci: VLAN TCI to insert * - * Puts the VLAN tag in @skb->cb[] and lets the device do the rest + * Puts the VLAN TCI in @skb->cb[] and lets the device do the rest */ -static inline struct sk_buff *__vlan_hwaccel_put_tag(struct sk_buff *skb, unsigned short tag) +static inline struct sk_buff *__vlan_hwaccel_put_tag(struct sk_buff *skb, + u16 vlan_tci) { struct vlan_skb_tx_cookie *cookie; cookie = VLAN_TX_SKB_CB(skb); cookie->magic = VLAN_TX_COOKIE_MAGIC; - cookie->vlan_tag = tag; + cookie->vlan_tag = vlan_tci; return skb; } @@ -228,28 +241,28 @@ static inline struct sk_buff *__vlan_hwaccel_put_tag(struct sk_buff *skb, unsign /** * vlan_put_tag - inserts VLAN tag according to device features * @skb: skbuff to tag - * @tag: VLAN tag to insert + * @vlan_tci: VLAN TCI to insert * * Assumes skb->dev is the target that will xmit this frame. * Returns a VLAN tagged skb. */ -static inline struct sk_buff *vlan_put_tag(struct sk_buff *skb, unsigned short tag) +static inline struct sk_buff *vlan_put_tag(struct sk_buff *skb, u16 vlan_tci) { if (skb->dev->features & NETIF_F_HW_VLAN_TX) { - return __vlan_hwaccel_put_tag(skb, tag); + return __vlan_hwaccel_put_tag(skb, vlan_tci); } else { - return __vlan_put_tag(skb, tag); + return __vlan_put_tag(skb, vlan_tci); } } /** * __vlan_get_tag - get the VLAN ID that is part of the payload * @skb: skbuff to query - * @tag: buffer to store vlaue - * + * @vlan_tci: buffer to store vlaue + * * Returns error if the skb is not of VLAN type */ -static inline int __vlan_get_tag(const struct sk_buff *skb, unsigned short *tag) +static inline int __vlan_get_tag(const struct sk_buff *skb, u16 *vlan_tci) { struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb->data; @@ -257,29 +270,28 @@ static inline int __vlan_get_tag(const struct sk_buff *skb, unsigned short *tag) return -EINVAL; } - *tag = ntohs(veth->h_vlan_TCI); - + *vlan_tci = ntohs(veth->h_vlan_TCI); return 0; } /** * __vlan_hwaccel_get_tag - get the VLAN ID that is in @skb->cb[] * @skb: skbuff to query - * @tag: buffer to store vlaue - * + * @vlan_tci: buffer to store vlaue + * * Returns error if @skb->cb[] is not set correctly */ static inline int __vlan_hwaccel_get_tag(const struct sk_buff *skb, - unsigned short *tag) + u16 *vlan_tci) { struct vlan_skb_tx_cookie *cookie; cookie = VLAN_TX_SKB_CB(skb); if (cookie->magic == VLAN_TX_COOKIE_MAGIC) { - *tag = cookie->vlan_tag; + *vlan_tci = cookie->vlan_tag; return 0; } else { - *tag = 0; + *vlan_tci = 0; return -EINVAL; } } @@ -289,16 +301,16 @@ static inline int __vlan_hwaccel_get_tag(const struct sk_buff *skb, /** * vlan_get_tag - get the VLAN ID from the skb * @skb: skbuff to query - * @tag: buffer to store vlaue - * + * @vlan_tci: buffer to store vlaue + * * Returns error if the skb is not VLAN tagged */ -static inline int vlan_get_tag(const struct sk_buff *skb, unsigned short *tag) +static inline int vlan_get_tag(const struct sk_buff *skb, u16 *vlan_tci) { if (skb->dev->features & NETIF_F_HW_VLAN_TX) { - return __vlan_hwaccel_get_tag(skb, tag); + return __vlan_hwaccel_get_tag(skb, vlan_tci); } else { - return __vlan_get_tag(skb, tag); + return __vlan_get_tag(skb, vlan_tci); } } diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 7a2625d2f9a0..68bdcf4a795c 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -83,13 +83,12 @@ static struct vlan_group *__vlan_find_group(struct net_device *real_dev) * * Must be invoked with RCU read lock (no preempt) */ -struct net_device *__find_vlan_dev(struct net_device *real_dev, - unsigned short VID) +struct net_device *__find_vlan_dev(struct net_device *real_dev, u16 vlan_id) { struct vlan_group *grp = __vlan_find_group(real_dev); if (grp) - return vlan_group_get_device(grp, VID); + return vlan_group_get_device(grp, vlan_id); return NULL; } @@ -117,14 +116,14 @@ static struct vlan_group *vlan_group_alloc(struct net_device *real_dev) return grp; } -static int vlan_group_prealloc_vid(struct vlan_group *vg, int vid) +static int vlan_group_prealloc_vid(struct vlan_group *vg, u16 vlan_id) { struct net_device **array; unsigned int size; ASSERT_RTNL(); - array = vg->vlan_devices_arrays[vid / VLAN_GROUP_ARRAY_PART_LEN]; + array = vg->vlan_devices_arrays[vlan_id / VLAN_GROUP_ARRAY_PART_LEN]; if (array != NULL) return 0; @@ -133,7 +132,7 @@ static int vlan_group_prealloc_vid(struct vlan_group *vg, int vid) if (array == NULL) return -ENOBUFS; - vg->vlan_devices_arrays[vid / VLAN_GROUP_ARRAY_PART_LEN] = array; + vg->vlan_devices_arrays[vlan_id / VLAN_GROUP_ARRAY_PART_LEN] = array; return 0; } @@ -147,7 +146,7 @@ void unregister_vlan_dev(struct net_device *dev) struct vlan_dev_info *vlan = vlan_dev_info(dev); struct net_device *real_dev = vlan->real_dev; struct vlan_group *grp; - unsigned short vlan_id = vlan->vlan_id; + u16 vlan_id = vlan->vlan_id; ASSERT_RTNL(); @@ -205,7 +204,7 @@ static void vlan_transfer_operstate(const struct net_device *dev, } } -int vlan_check_real_dev(struct net_device *real_dev, unsigned short vlan_id) +int vlan_check_real_dev(struct net_device *real_dev, u16 vlan_id) { char *name = real_dev->name; @@ -242,7 +241,7 @@ int register_vlan_dev(struct net_device *dev) { struct vlan_dev_info *vlan = vlan_dev_info(dev); struct net_device *real_dev = vlan->real_dev; - unsigned short vlan_id = vlan->vlan_id; + u16 vlan_id = vlan->vlan_id; struct vlan_group *grp, *ngrp = NULL; int err; @@ -295,8 +294,7 @@ out_free_group: /* Attach a VLAN device to a mac address (ie Ethernet Card). * Returns 0 if the device was created or a negative error code otherwise. */ -static int register_vlan_device(struct net_device *real_dev, - unsigned short VLAN_ID) +static int register_vlan_device(struct net_device *real_dev, u16 vlan_id) { struct net_device *new_dev; struct net *net = dev_net(real_dev); @@ -304,10 +302,10 @@ static int register_vlan_device(struct net_device *real_dev, char name[IFNAMSIZ]; int err; - if (VLAN_ID >= VLAN_VID_MASK) + if (vlan_id >= VLAN_VID_MASK) return -ERANGE; - err = vlan_check_real_dev(real_dev, VLAN_ID); + err = vlan_check_real_dev(real_dev, vlan_id); if (err < 0) return err; @@ -315,26 +313,26 @@ static int register_vlan_device(struct net_device *real_dev, switch (vn->name_type) { case VLAN_NAME_TYPE_RAW_PLUS_VID: /* name will look like: eth1.0005 */ - snprintf(name, IFNAMSIZ, "%s.%.4i", real_dev->name, VLAN_ID); + snprintf(name, IFNAMSIZ, "%s.%.4i", real_dev->name, vlan_id); break; case VLAN_NAME_TYPE_PLUS_VID_NO_PAD: /* Put our vlan.VID in the name. * Name will look like: vlan5 */ - snprintf(name, IFNAMSIZ, "vlan%i", VLAN_ID); + snprintf(name, IFNAMSIZ, "vlan%i", vlan_id); break; case VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD: /* Put our vlan.VID in the name. * Name will look like: eth0.5 */ - snprintf(name, IFNAMSIZ, "%s.%i", real_dev->name, VLAN_ID); + snprintf(name, IFNAMSIZ, "%s.%i", real_dev->name, vlan_id); break; case VLAN_NAME_TYPE_PLUS_VID: /* Put our vlan.VID in the name. * Name will look like: vlan0005 */ default: - snprintf(name, IFNAMSIZ, "vlan%.4i", VLAN_ID); + snprintf(name, IFNAMSIZ, "vlan%.4i", vlan_id); } new_dev = alloc_netdev(sizeof(struct vlan_dev_info), name, @@ -349,7 +347,7 @@ static int register_vlan_device(struct net_device *real_dev, */ new_dev->mtu = real_dev->mtu; - vlan_dev_info(new_dev)->vlan_id = VLAN_ID; /* 1 through VLAN_VID_MASK */ + vlan_dev_info(new_dev)->vlan_id = vlan_id; vlan_dev_info(new_dev)->real_dev = real_dev; vlan_dev_info(new_dev)->dent = NULL; vlan_dev_info(new_dev)->flags = VLAN_FLAG_REORDER_HDR; diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h index 14c421e033f2..a6603a4d917f 100644 --- a/net/8021q/vlan.h +++ b/net/8021q/vlan.h @@ -12,7 +12,7 @@ */ struct vlan_priority_tci_mapping { u32 priority; - unsigned short vlan_qos; + u16 vlan_qos; struct vlan_priority_tci_mapping *next; }; @@ -36,8 +36,8 @@ struct vlan_dev_info { unsigned int nr_egress_mappings; struct vlan_priority_tci_mapping *egress_priority_map[16]; - unsigned short vlan_id; - unsigned short flags; + u16 vlan_id; + u16 flags; struct net_device *real_dev; unsigned char real_dev_addr[ETH_ALEN]; @@ -67,30 +67,29 @@ static inline struct vlan_dev_info *vlan_dev_info(const struct net_device *dev) * Must be invoked with rcu_read_lock (ie preempt disabled) * or with RTNL. */ -struct net_device *__find_vlan_dev(struct net_device *real_dev, - unsigned short VID); /* vlan.c */ +struct net_device *__find_vlan_dev(struct net_device *real_dev, u16 vlan_id); /* found in vlan_dev.c */ int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype, struct net_device *orig_dev); void vlan_dev_set_ingress_priority(const struct net_device *dev, - u32 skb_prio, short vlan_prio); + u32 skb_prio, u16 vlan_prio); int vlan_dev_set_egress_priority(const struct net_device *dev, - u32 skb_prio, short vlan_prio); + u32 skb_prio, u16 vlan_prio); int vlan_dev_change_flags(const struct net_device *dev, u32 flag, u32 mask); void vlan_dev_get_realdev_name(const struct net_device *dev, char *result); -int vlan_check_real_dev(struct net_device *real_dev, unsigned short vlan_id); +int vlan_check_real_dev(struct net_device *real_dev, u16 vlan_id); void vlan_setup(struct net_device *dev); int register_vlan_dev(struct net_device *dev); void unregister_vlan_dev(struct net_device *dev); static inline u32 vlan_get_ingress_priority(struct net_device *dev, - unsigned short vlan_tag) + u16 vlan_tci) { struct vlan_dev_info *vip = vlan_dev_info(dev); - return vip->ingress_priority_map[(vlan_tag >> 13) & 0x7]; + return vip->ingress_priority_map[(vlan_tci >> 13) & 0x7]; } #ifdef CONFIG_VLAN_8021Q_GVRP diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c index f980b9154cc3..68df12d3664b 100644 --- a/net/8021q/vlan_core.c +++ b/net/8021q/vlan_core.c @@ -5,7 +5,7 @@ /* VLAN rx hw acceleration helper. This acts like netif_{rx,receive_skb}(). */ int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, - unsigned short vlan_tag, int polling) + u16 vlan_tci, int polling) { struct net_device_stats *stats; @@ -14,7 +14,7 @@ int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, return NET_RX_DROP; } - skb->dev = vlan_group_get_device(grp, vlan_tag & VLAN_VID_MASK); + skb->dev = vlan_group_get_device(grp, vlan_tci & VLAN_VID_MASK); if (skb->dev == NULL) { dev_kfree_skb_any(skb); /* Not NET_RX_DROP, this is not being dropped @@ -27,7 +27,7 @@ int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, stats->rx_packets++; stats->rx_bytes += skb->len; - skb->priority = vlan_get_ingress_priority(skb->dev, vlan_tag); + skb->priority = vlan_get_ingress_priority(skb->dev, vlan_tci); switch (skb->pkt_type) { case PACKET_BROADCAST: break; diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 2aab294c5744..2ccac6bea57e 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -150,9 +150,9 @@ int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype, struct net_device *orig_dev) { struct vlan_hdr *vhdr; - unsigned short vid; struct net_device_stats *stats; - unsigned short vlan_TCI; + u16 vlan_id; + u16 vlan_tci; skb = skb_share_check(skb, GFP_ATOMIC); if (skb == NULL) @@ -162,14 +162,14 @@ int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev, goto err_free; vhdr = (struct vlan_hdr *)skb->data; - vlan_TCI = ntohs(vhdr->h_vlan_TCI); - vid = (vlan_TCI & VLAN_VID_MASK); + vlan_tci = ntohs(vhdr->h_vlan_TCI); + vlan_id = vlan_tci & VLAN_VID_MASK; rcu_read_lock(); - skb->dev = __find_vlan_dev(dev, vid); + skb->dev = __find_vlan_dev(dev, vlan_id); if (!skb->dev) { pr_debug("%s: ERROR: No net_device for VID: %u on dev: %s\n", - __func__, (unsigned int)vid, dev->name); + __func__, vlan_id, dev->name); goto err_unlock; } @@ -181,11 +181,10 @@ int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev, skb_pull_rcsum(skb, VLAN_HLEN); - skb->priority = vlan_get_ingress_priority(skb->dev, - ntohs(vhdr->h_vlan_TCI)); + skb->priority = vlan_get_ingress_priority(skb->dev, vlan_tci); pr_debug("%s: priority: %u for TCI: %hu\n", - __func__, skb->priority, ntohs(vhdr->h_vlan_TCI)); + __func__, skb->priority, vlan_tci); switch (skb->pkt_type) { case PACKET_BROADCAST: /* Yeah, stats collect these together.. */ @@ -228,7 +227,7 @@ err_free: return NET_RX_DROP; } -static inline unsigned short +static inline u16 vlan_dev_get_egress_qos_mask(struct net_device *dev, struct sk_buff *skb) { struct vlan_priority_tci_mapping *mp; @@ -260,7 +259,7 @@ static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev, unsigned int len) { struct vlan_hdr *vhdr; - unsigned short veth_TCI = 0; + u16 vlan_tci = 0; int rc = 0; int build_vlan_header = 0; struct net_device *vdev = dev; @@ -292,10 +291,10 @@ static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev, * VLAN ID 12 bits (low bits) * */ - veth_TCI = vlan_dev_info(dev)->vlan_id; - veth_TCI |= vlan_dev_get_egress_qos_mask(dev, skb); + vlan_tci = vlan_dev_info(dev)->vlan_id; + vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb); - vhdr->h_vlan_TCI = htons(veth_TCI); + vhdr->h_vlan_TCI = htons(vlan_tci); /* * Set the protocol type. For a packet of type ETH_P_802_3 we @@ -373,7 +372,7 @@ static int vlan_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) if (veth->h_vlan_proto != htons(ETH_P_8021Q) || vlan_dev_info(dev)->flags & VLAN_FLAG_REORDER_HDR) { int orig_headroom = skb_headroom(skb); - unsigned short veth_TCI; + u16 vlan_tci; /* This is not a VLAN frame...but we can fix that! */ vlan_dev_info(dev)->cnt_encap_on_xmit++; @@ -386,10 +385,10 @@ static int vlan_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) * CFI 1 bit * VLAN ID 12 bits (low bits) */ - veth_TCI = vlan_dev_info(dev)->vlan_id; - veth_TCI |= vlan_dev_get_egress_qos_mask(dev, skb); + vlan_tci = vlan_dev_info(dev)->vlan_id; + vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb); - skb = __vlan_put_tag(skb, veth_TCI); + skb = __vlan_put_tag(skb, vlan_tci); if (!skb) { stats->tx_dropped++; return 0; @@ -422,7 +421,7 @@ static int vlan_dev_hwaccel_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct net_device_stats *stats = &dev->stats; - unsigned short veth_TCI; + u16 vlan_tci; /* Construct the second two bytes. This field looks something * like: @@ -430,9 +429,9 @@ static int vlan_dev_hwaccel_hard_start_xmit(struct sk_buff *skb, * CFI 1 bit * VLAN ID 12 bits (low bits) */ - veth_TCI = vlan_dev_info(dev)->vlan_id; - veth_TCI |= vlan_dev_get_egress_qos_mask(dev, skb); - skb = __vlan_hwaccel_put_tag(skb, veth_TCI); + vlan_tci = vlan_dev_info(dev)->vlan_id; + vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb); + skb = __vlan_hwaccel_put_tag(skb, vlan_tci); stats->tx_packets++; stats->tx_bytes += skb->len; @@ -457,7 +456,7 @@ static int vlan_dev_change_mtu(struct net_device *dev, int new_mtu) } void vlan_dev_set_ingress_priority(const struct net_device *dev, - u32 skb_prio, short vlan_prio) + u32 skb_prio, u16 vlan_prio) { struct vlan_dev_info *vlan = vlan_dev_info(dev); @@ -470,7 +469,7 @@ void vlan_dev_set_ingress_priority(const struct net_device *dev, } int vlan_dev_set_egress_priority(const struct net_device *dev, - u32 skb_prio, short vlan_prio) + u32 skb_prio, u16 vlan_prio) { struct vlan_dev_info *vlan = vlan_dev_info(dev); struct vlan_priority_tci_mapping *mp = NULL; diff --git a/net/8021q/vlan_gvrp.c b/net/8021q/vlan_gvrp.c index db9781608362..061ceceeef12 100644 --- a/net/8021q/vlan_gvrp.c +++ b/net/8021q/vlan_gvrp.c @@ -30,19 +30,19 @@ static struct garp_application vlan_gvrp_app __read_mostly = { int vlan_gvrp_request_join(const struct net_device *dev) { const struct vlan_dev_info *vlan = vlan_dev_info(dev); - __be16 vid = htons(vlan->vlan_id); + __be16 vlan_id = htons(vlan->vlan_id); return garp_request_join(vlan->real_dev, &vlan_gvrp_app, - &vid, sizeof(vid), GVRP_ATTR_VID); + &vlan_id, sizeof(vlan_id), GVRP_ATTR_VID); } void vlan_gvrp_request_leave(const struct net_device *dev) { const struct vlan_dev_info *vlan = vlan_dev_info(dev); - __be16 vid = htons(vlan->vlan_id); + __be16 vlan_id = htons(vlan->vlan_id); garp_request_leave(vlan->real_dev, &vlan_gvrp_app, - &vid, sizeof(vid), GVRP_ATTR_VID); + &vlan_id, sizeof(vlan_id), GVRP_ATTR_VID); } int vlan_gvrp_init_applicant(struct net_device *dev) -- cgit v1.2.3 From 429a380571a6e6b8525b93161544eafc9b227e44 Mon Sep 17 00:00:00 2001 From: Ron Rindjunsky Date: Tue, 1 Jul 2008 14:16:03 +0300 Subject: mac80211: add block ack request capability This patch adds block ack request capability Signed-off-by: Ester Kummer Signed-off-by: Tomas Winkler Signed-off-by: Ron Rindjunsky Acked-by: Johannes Berg Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 4 ++++ include/net/mac80211.h | 3 +++ net/mac80211/ieee80211_i.h | 1 + net/mac80211/main.c | 21 +++++++++++++++++++-- net/mac80211/mlme.c | 29 +++++++++++++++++++++++++++++ 5 files changed, 56 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index cffd6d0094f9..aa603c3d76d1 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -658,6 +658,10 @@ struct ieee80211_bar { __le16 start_seq_num; } __attribute__((packed)); +/* 802.11 BAR control masks */ +#define IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL 0x0000 +#define IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA 0x0004 + /** * struct ieee80211_ht_cap - HT capabilities * diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 3a204acad901..0a5de3ef527a 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -235,6 +235,8 @@ struct ieee80211_bss_conf { * @IEEE80211_TX_STAT_ACK: Frame was acknowledged * @IEEE80211_TX_STAT_AMPDU: The frame was aggregated, so status * is for the whole aggregation. + * @IEEE80211_TX_STAT_AMPDU_NO_BACK: no block ack was returned, + * so consider using block ack request (BAR). */ enum mac80211_tx_control_flags { IEEE80211_TX_CTL_REQ_TX_STATUS = BIT(0), @@ -260,6 +262,7 @@ enum mac80211_tx_control_flags { IEEE80211_TX_STAT_TX_FILTERED = BIT(20), IEEE80211_TX_STAT_ACK = BIT(21), IEEE80211_TX_STAT_AMPDU = BIT(22), + IEEE80211_TX_STAT_AMPDU_NO_BACK = BIT(23), }; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index f90da1bbec49..175cbdd36d7f 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -904,6 +904,7 @@ void ieee80211_send_addba_request(struct net_device *dev, const u8 *da, u16 agg_size, u16 timeout); void ieee80211_send_delba(struct net_device *dev, const u8 *da, u16 tid, u16 initiator, u16 reason_code); +void ieee80211_send_bar(struct net_device *dev, u8 *ra, u16 tid, u16 ssn); void ieee80211_sta_stop_rx_ba_session(struct net_device *dev, u8 *da, u16 tid, u16 initiator, u16 reason); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index f18cfd727872..074f71a62a6d 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1404,14 +1404,15 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); u16 frag, type; + __le16 fc; struct ieee80211_tx_status_rtap_hdr *rthdr; struct ieee80211_sub_if_data *sdata; struct net_device *prev_dev = NULL; + struct sta_info *sta; rcu_read_lock(); if (info->status.excessive_retries) { - struct sta_info *sta; sta = sta_info_get(local, hdr->addr1); if (sta) { if (test_sta_flags(sta, WLAN_STA_PS)) { @@ -1426,8 +1427,24 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) } } + fc = hdr->frame_control; + + if ((info->flags & IEEE80211_TX_STAT_AMPDU_NO_BACK) && + (ieee80211_is_data_qos(fc))) { + u16 tid, ssn; + u8 *qc; + sta = sta_info_get(local, hdr->addr1); + if (sta) { + qc = ieee80211_get_qos_ctl(hdr); + tid = qc[0] & 0xf; + ssn = ((le16_to_cpu(hdr->seq_ctrl) + 0x10) + & IEEE80211_SCTL_SEQ); + ieee80211_send_bar(sta->sdata->dev, hdr->addr1, + tid, ssn); + } + } + if (info->flags & IEEE80211_TX_STAT_TX_FILTERED) { - struct sta_info *sta; sta = sta_info_get(local, hdr->addr1); if (sta) { ieee80211_handle_filtered_frame(local, sta, skb); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 86abdf96390c..e080482b63cd 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1536,6 +1536,35 @@ void ieee80211_send_delba(struct net_device *dev, const u8 *da, u16 tid, ieee80211_sta_tx(dev, skb, 0); } +void ieee80211_send_bar(struct net_device *dev, u8 *ra, u16 tid, u16 ssn) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct sk_buff *skb; + struct ieee80211_bar *bar; + u16 bar_control = 0; + + skb = dev_alloc_skb(sizeof(*bar) + local->hw.extra_tx_headroom); + if (!skb) { + printk(KERN_ERR "%s: failed to allocate buffer for " + "bar frame\n", dev->name); + return; + } + skb_reserve(skb, local->hw.extra_tx_headroom); + bar = (struct ieee80211_bar *)skb_put(skb, sizeof(*bar)); + memset(bar, 0, sizeof(*bar)); + bar->frame_control = IEEE80211_FC(IEEE80211_FTYPE_CTL, + IEEE80211_STYPE_BACK_REQ); + memcpy(bar->ra, ra, ETH_ALEN); + memcpy(bar->ta, dev->dev_addr, ETH_ALEN); + bar_control |= (u16)IEEE80211_BAR_CTRL_ACK_POLICY_NORMAL; + bar_control |= (u16)IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA; + bar_control |= (u16)(tid << 12); + bar->control = cpu_to_le16(bar_control); + bar->start_seq_num = cpu_to_le16(ssn); + + ieee80211_sta_tx(dev, skb, 0); +} + void ieee80211_sta_stop_rx_ba_session(struct net_device *dev, u8 *ra, u16 tid, u16 initiator, u16 reason) { -- cgit v1.2.3 From f3d1eb19abdcb1e740d8ba0e06d606c1d4165438 Mon Sep 17 00:00:00 2001 From: Niels de Vos Date: Tue, 8 Jul 2008 10:30:27 -0400 Subject: Input: serio - trivial documentation fix In include/linux/serio.h two different define-series are documented as "Serio types". However the second series contains defines for the different protocols. Signed-off-by: Niels de Vos Signed-off-by: Dmitry Torokhov --- include/linux/serio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/serio.h b/include/linux/serio.h index 48defc4d181e..e72716cca577 100644 --- a/include/linux/serio.h +++ b/include/linux/serio.h @@ -175,7 +175,7 @@ static inline void serio_unpin_driver(struct serio *serio) #define SERIO_8042_XL 0x06 /* - * Serio types + * Serio protocols */ #define SERIO_UNKNOWN 0x00 #define SERIO_MSC 0x01 -- cgit v1.2.3 From 238f74a227fd7de8ea1bc66dcbbd36cf9920d1cb Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Wed, 2 Jul 2008 11:05:34 -0700 Subject: mac80211: move QOS control helpers into ieee80211.h Also remove the WLAN_IS_QOS_DATA inline after removing the last two users. This starts moving away from using rx->fc to using the header frame_control directly. Signed-off-by: Harvey Harrison Signed-off-by: John W. Linville --- include/linux/ieee80211.h | 2 ++ net/mac80211/rx.c | 28 ++++++++++++++-------------- net/mac80211/wme.c | 4 ++-- net/mac80211/wme.h | 8 -------- 4 files changed, 18 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index aa603c3d76d1..a1630ba0b87c 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -99,6 +99,8 @@ #define IEEE80211_MAX_SSID_LEN 32 #define IEEE80211_MAX_MESH_ID_LEN 32 #define IEEE80211_QOS_CTL_LEN 2 +#define IEEE80211_QOS_CTL_TID_MASK 0x000F +#define IEEE80211_QOS_CTL_TAG1D_MASK 0x0007 struct ieee80211_hdr { __le16 frame_control; diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 6a88e8f9bff0..a3a26e557274 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -321,20 +321,20 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, static void ieee80211_parse_qos(struct ieee80211_rx_data *rx) { - u8 *data = rx->skb->data; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; int tid; /* does the frame have a qos control field? */ - if (WLAN_FC_IS_QOS_DATA(rx->fc)) { - u8 *qc = data + ieee80211_get_hdrlen(rx->fc) - QOS_CONTROL_LEN; + if (ieee80211_is_data_qos(hdr->frame_control)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); /* frame has qos control */ - tid = qc[0] & QOS_CONTROL_TID_MASK; - if (qc[0] & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT) + tid = *qc & IEEE80211_QOS_CTL_TID_MASK; + if (*qc & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT) rx->flags |= IEEE80211_RX_AMSDU; else rx->flags &= ~IEEE80211_RX_AMSDU; } else { - if (unlikely((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)) { + if (unlikely(ieee80211_is_mgmt(hdr->frame_control))) { /* Separate TID for management frames */ tid = NUM_RX_DATA_QUEUES - 1; } else { @@ -1037,19 +1037,19 @@ ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx) static ieee80211_rx_result debug_noinline ieee80211_rx_h_remove_qos_control(struct ieee80211_rx_data *rx) { - u16 fc = rx->fc; u8 *data = rx->skb->data; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) data; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)data; - if (!WLAN_FC_IS_QOS_DATA(fc)) + if (!ieee80211_is_data_qos(hdr->frame_control)) return RX_CONTINUE; /* remove the qos control field, update frame type and meta-data */ - memmove(data + 2, data, ieee80211_get_hdrlen(fc) - 2); - hdr = (struct ieee80211_hdr *) skb_pull(rx->skb, 2); + memmove(data + IEEE80211_QOS_CTL_LEN, data, + ieee80211_hdrlen(hdr->frame_control) - IEEE80211_QOS_CTL_LEN); + hdr = (struct ieee80211_hdr *)skb_pull(rx->skb, IEEE80211_QOS_CTL_LEN); /* change frame type to non QOS */ - rx->fc = fc &= ~IEEE80211_STYPE_QOS_DATA; - hdr->frame_control = cpu_to_le16(fc); + rx->fc &= ~IEEE80211_STYPE_QOS_DATA; + hdr->frame_control &= ~cpu_to_le16(IEEE80211_STYPE_QOS_DATA); return RX_CONTINUE; } @@ -2044,7 +2044,7 @@ static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local, if (!ieee80211_is_data_qos(hdr->frame_control)) goto end_reorder; - tid = *ieee80211_get_qos_ctl(hdr) & QOS_CONTROL_TID_MASK; + tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; if (sta->ampdu_mlme.tid_state_rx[tid] != HT_AGG_STATE_OPERATIONAL) goto end_reorder; diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index a1a53a4f2b99..5c666f7eda8f 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -154,7 +154,7 @@ static int wme_qdiscop_enqueue(struct sk_buff *skb, struct Qdisc* qd) queue = skb_get_queue_mapping(skb); rcu_read_lock(); sta = sta_info_get(local, hdr->addr1); - tid = skb->priority & QOS_CONTROL_TAG1D_MASK; + tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; if (sta) { int ampdu_queue = sta->tid_to_tx_q[tid]; if ((ampdu_queue < QD_NUM(hw)) && @@ -181,7 +181,7 @@ static int wme_qdiscop_enqueue(struct sk_buff *skb, struct Qdisc* qd) if (ieee80211_is_data_qos(hdr->frame_control)) { u8 *p = ieee80211_get_qos_ctl(hdr); u8 ack_policy = 0; - tid = skb->priority & QOS_CONTROL_TAG1D_MASK; + tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; if (local->wifi_wme_noack_test) ack_policy |= QOS_CONTROL_ACK_POLICY_NOACK << QOS_CONTROL_ACK_POLICY_SHIFT; diff --git a/net/mac80211/wme.h b/net/mac80211/wme.h index bbdb53344817..1aca609eccfc 100644 --- a/net/mac80211/wme.h +++ b/net/mac80211/wme.h @@ -19,18 +19,10 @@ #define QOS_CONTROL_ACK_POLICY_NORMAL 0 #define QOS_CONTROL_ACK_POLICY_NOACK 1 -#define QOS_CONTROL_TID_MASK 0x0f #define QOS_CONTROL_ACK_POLICY_SHIFT 5 -#define QOS_CONTROL_TAG1D_MASK 0x07 - extern const int ieee802_1d_to_ac[8]; -static inline int WLAN_FC_IS_QOS_DATA(u16 fc) -{ - return (fc & 0x8C) == 0x88; -} - #ifdef CONFIG_MAC80211_QOS void ieee80211_install_qdisc(struct net_device *dev); int ieee80211_qdisc_installed(struct net_device *dev); -- cgit v1.2.3 From 11a100f844f6096787ab20e19f17d72abc957a8f Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Tue, 8 Jul 2008 15:36:57 -0700 Subject: vlan: avoid header copying and linearisation where possible - vlan_dev_reorder_header() is only called on the receive path after calling skb_share_check(). This means we can use skb_cow() since all we need is a writable header. - vlan_dev_hard_header() includes a work-around for some apparently broken out of tree MPLS code. The hard_header functions can expect to always have a headroom of at least there own hard_header_len available, so the reallocation check is unnecessary. - __vlan_put_tag() can use skb_cow_head() to avoid the skb_unshare() copy when the header is writable. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/if_vlan.h | 18 +++--------------- net/8021q/vlan_dev.c | 34 +++++----------------------------- 2 files changed, 8 insertions(+), 44 deletions(-) (limited to 'include/linux') diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index d36515dae62f..93f5d9b0e9f9 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -185,22 +185,10 @@ static inline struct sk_buff *__vlan_put_tag(struct sk_buff *skb, u16 vlan_tci) { struct vlan_ethhdr *veth; - if (skb_headroom(skb) < VLAN_HLEN) { - struct sk_buff *sk_tmp = skb; - skb = skb_realloc_headroom(sk_tmp, VLAN_HLEN); - kfree_skb(sk_tmp); - if (!skb) { - printk(KERN_ERR "vlan: failed to realloc headroom\n"); - return NULL; - } - } else { - skb = skb_unshare(skb, GFP_ATOMIC); - if (!skb) { - printk(KERN_ERR "vlan: failed to unshare skbuff\n"); - return NULL; - } + if (skb_cow_head(skb, VLAN_HLEN) < 0) { + kfree_skb(skb); + return NULL; } - veth = (struct vlan_ethhdr *)skb_push(skb, VLAN_HLEN); /* Move the mac addresses to the beginning of the new header. */ diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 2ccac6bea57e..b6e52c025fd8 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -74,11 +74,8 @@ static int vlan_dev_rebuild_header(struct sk_buff *skb) static inline struct sk_buff *vlan_check_reorder_header(struct sk_buff *skb) { if (vlan_dev_info(skb->dev)->flags & VLAN_FLAG_REORDER_HDR) { - if (skb_shared(skb) || skb_cloned(skb)) { - struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC); - kfree_skb(skb); - skb = nskb; - } + if (skb_cow(skb, skb_headroom(skb)) < 0) + skb = NULL; if (skb) { /* Lifted from Gleb's VLAN code... */ memmove(skb->data - ETH_HLEN, @@ -262,12 +259,14 @@ static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev, u16 vlan_tci = 0; int rc = 0; int build_vlan_header = 0; - struct net_device *vdev = dev; pr_debug("%s: skb: %p type: %hx len: %u vlan_id: %hx, daddr: %p\n", __func__, skb, type, len, vlan_dev_info(dev)->vlan_id, daddr); + if (WARN_ON(skb_headroom(skb) < dev->hard_header_len)) + return -ENOSPC; + /* build vlan header only if re_order_header flag is NOT set. This * fixes some programs that get confused when they see a VLAN device * sending a frame that is VLAN encoded (the consensus is that the VLAN @@ -316,29 +315,6 @@ static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev, dev = vlan_dev_info(dev)->real_dev; - /* MPLS can send us skbuffs w/out enough space. This check will grow - * the skb if it doesn't have enough headroom. Not a beautiful solution, - * so I'll tick a counter so that users can know it's happening... - * If they care... - */ - - /* NOTE: This may still break if the underlying device is not the final - * device (and thus there are more headers to add...) It should work for - * good-ole-ethernet though. - */ - if (skb_headroom(skb) < dev->hard_header_len) { - struct sk_buff *sk_tmp = skb; - skb = skb_realloc_headroom(sk_tmp, dev->hard_header_len); - kfree_skb(sk_tmp); - if (skb == NULL) { - struct net_device_stats *stats = &vdev->stats; - stats->tx_dropped++; - return -ENOMEM; - } - vlan_dev_info(vdev)->cnt_inc_headroom_on_tx++; - pr_debug("%s: %s: had to grow skb\n", __func__, vdev->name); - } - if (build_vlan_header) { /* Now make the underlying real hard header */ rc = dev_hard_header(skb, dev, ETH_P_8021Q, daddr, saddr, -- cgit v1.2.3 From bb949fbd1878973c3539d9aecff52f284482a937 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 8 Jul 2008 16:55:56 -0700 Subject: netdev: Create netdev_queue abstraction. A netdev_queue is an entity managed by a qdisc. Currently there is one RX and one TX queue, and a netdev_queue merely contains a backpointer to the net_device. The Qdisc struct is augmented with a netdev_queue pointer as well. Eventually the 'dev' Qdisc member will go away and we will have the resulting hierarchy: net_device --> netdev_queue --> Qdisc Also, qdisc_alloc() and qdisc_create_dflt() now take a netdev_queue pointer argument. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 7 +++++++ include/net/sch_generic.h | 6 +++++- net/core/dev.c | 8 ++++++++ net/mac80211/wme.c | 6 ++++-- net/sched/sch_api.c | 12 +++++++----- net/sched/sch_atm.c | 6 ++++-- net/sched/sch_cbq.c | 9 ++++++--- net/sched/sch_dsmark.c | 6 ++++-- net/sched/sch_fifo.c | 3 ++- net/sched/sch_generic.c | 14 ++++++++++---- net/sched/sch_hfsc.c | 9 ++++++--- net/sched/sch_htb.c | 11 +++++++---- net/sched/sch_netem.c | 3 ++- net/sched/sch_prio.c | 3 ++- 14 files changed, 74 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index e009c6fbf5cd..515fd25bf0fc 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -448,6 +448,10 @@ static inline void napi_synchronize(const struct napi_struct *n) # define napi_synchronize(n) barrier() #endif +struct netdev_queue { + struct net_device *dev; +}; + /* * The DEVICE structure. * Actually, this whole structure is a big mistake. It mixes I/O @@ -624,6 +628,9 @@ struct net_device unsigned char broadcast[MAX_ADDR_LEN]; /* hw bcast add */ + struct netdev_queue rx_queue; + struct netdev_queue tx_queue; + /* ingress path synchronizer */ spinlock_t ingress_lock; struct Qdisc *qdisc_ingress; diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 073f2580b83b..0ab53c575f87 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -37,6 +37,7 @@ struct Qdisc u32 parent; atomic_t refcnt; struct sk_buff_head q; + struct netdev_queue *dev_queue; struct net_device *dev; struct list_head list; @@ -216,8 +217,11 @@ extern void dev_deactivate(struct net_device *dev); extern void qdisc_reset(struct Qdisc *qdisc); extern void qdisc_destroy(struct Qdisc *qdisc); extern void qdisc_tree_decrease_qlen(struct Qdisc *qdisc, unsigned int n); -extern struct Qdisc *qdisc_alloc(struct net_device *dev, struct Qdisc_ops *ops); +extern struct Qdisc *qdisc_alloc(struct net_device *dev, + struct netdev_queue *dev_queue, + struct Qdisc_ops *ops); extern struct Qdisc *qdisc_create_dflt(struct net_device *dev, + struct netdev_queue *dev_queue, struct Qdisc_ops *ops, u32 parentid); extern void tcf_destroy(struct tcf_proto *tp); extern void tcf_destroy_chain(struct tcf_proto **fl); diff --git a/net/core/dev.c b/net/core/dev.c index 75933932463d..9b281c906eb0 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4072,6 +4072,12 @@ static struct net_device_stats *internal_stats(struct net_device *dev) return &dev->stats; } +static void netdev_init_queues(struct net_device *dev) +{ + dev->rx_queue.dev = dev; + dev->tx_queue.dev = dev; +} + /** * alloc_netdev_mq - allocate network device * @sizeof_priv: size of private data to allocate space for @@ -4124,6 +4130,8 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, dev->egress_subqueue_count = queue_count; dev->gso_max_size = GSO_MAX_SIZE; + netdev_init_queues(dev); + dev->get_stats = internal_stats; netpoll_netdev_init(dev); setup(dev); diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 5c666f7eda8f..770f1c09b793 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -359,7 +359,8 @@ static int wme_qdiscop_init(struct Qdisc *qd, struct nlattr *opt) /* create child queues */ for (i = 0; i < QD_NUM(hw); i++) { skb_queue_head_init(&q->requeued[i]); - q->queues[i] = qdisc_create_dflt(qd->dev, &pfifo_qdisc_ops, + q->queues[i] = qdisc_create_dflt(qd->dev, qd->dev_queue, + &pfifo_qdisc_ops, qd->handle); if (!q->queues[i]) { q->queues[i] = &noop_qdisc; @@ -575,7 +576,8 @@ void ieee80211_install_qdisc(struct net_device *dev) { struct Qdisc *qdisc; - qdisc = qdisc_create_dflt(dev, &wme_qdisc_ops, TC_H_ROOT); + qdisc = qdisc_create_dflt(dev, &dev->tx_queue, + &wme_qdisc_ops, TC_H_ROOT); if (!qdisc) { printk(KERN_ERR "%s: qdisc installation failed\n", dev->name); return; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 69e918bb4278..b86c98bd06a3 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -552,8 +552,8 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent, */ static struct Qdisc * -qdisc_create(struct net_device *dev, u32 parent, u32 handle, - struct nlattr **tca, int *errp) +qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue, + u32 parent, u32 handle, struct nlattr **tca, int *errp) { int err; struct nlattr *kind = tca[TCA_KIND]; @@ -593,7 +593,7 @@ qdisc_create(struct net_device *dev, u32 parent, u32 handle, if (ops == NULL) goto err_out; - sch = qdisc_alloc(dev, ops); + sch = qdisc_alloc(dev, dev_queue, ops); if (IS_ERR(sch)) { err = PTR_ERR(sch); goto err_out2; @@ -892,10 +892,12 @@ create_n_graft: if (!(n->nlmsg_flags&NLM_F_CREATE)) return -ENOENT; if (clid == TC_H_INGRESS) - q = qdisc_create(dev, tcm->tcm_parent, tcm->tcm_parent, + q = qdisc_create(dev, &dev->rx_queue, + tcm->tcm_parent, tcm->tcm_parent, tca, &err); else - q = qdisc_create(dev, tcm->tcm_parent, tcm->tcm_handle, + q = qdisc_create(dev, &dev->tx_queue, + tcm->tcm_parent, tcm->tcm_handle, tca, &err); if (q == NULL) { if (err == -EAGAIN) diff --git a/net/sched/sch_atm.c b/net/sched/sch_atm.c index db0e23ae85f8..3dddab531d5a 100644 --- a/net/sched/sch_atm.c +++ b/net/sched/sch_atm.c @@ -296,7 +296,8 @@ static int atm_tc_change(struct Qdisc *sch, u32 classid, u32 parent, goto err_out; } flow->filter_list = NULL; - flow->q = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, classid); + flow->q = qdisc_create_dflt(sch->dev, sch->dev_queue, + &pfifo_qdisc_ops, classid); if (!flow->q) flow->q = &noop_qdisc; pr_debug("atm_tc_change: qdisc %p\n", flow->q); @@ -555,7 +556,8 @@ static int atm_tc_init(struct Qdisc *sch, struct nlattr *opt) pr_debug("atm_tc_init(sch %p,[qdisc %p],opt %p)\n", sch, p, opt); p->flows = &p->link; - p->link.q = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, sch->handle); + p->link.q = qdisc_create_dflt(sch->dev, sch->dev_queue, + &pfifo_qdisc_ops, sch->handle); if (!p->link.q) p->link.q = &noop_qdisc; pr_debug("atm_tc_init: link (%p) qdisc %p\n", &p->link, p->link.q); diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index 968b4c73c9c1..d360dcd0818b 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -1401,7 +1401,8 @@ static int cbq_init(struct Qdisc *sch, struct nlattr *opt) q->link.sibling = &q->link; q->link.common.classid = sch->handle; q->link.qdisc = sch; - if (!(q->link.q = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, + if (!(q->link.q = qdisc_create_dflt(sch->dev, sch->dev_queue, + &pfifo_qdisc_ops, sch->handle))) q->link.q = &noop_qdisc; @@ -1645,7 +1646,8 @@ static int cbq_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, if (cl) { if (new == NULL) { - new = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, + new = qdisc_create_dflt(sch->dev, sch->dev_queue, + &pfifo_qdisc_ops, cl->common.classid); if (new == NULL) return -ENOBUFS; @@ -1877,7 +1879,8 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t cl->R_tab = rtab; rtab = NULL; cl->refcnt = 1; - if (!(cl->q = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, classid))) + if (!(cl->q = qdisc_create_dflt(sch->dev, sch->dev_queue, + &pfifo_qdisc_ops, classid))) cl->q = &noop_qdisc; cl->common.classid = classid; cl->tparent = parent; diff --git a/net/sched/sch_dsmark.c b/net/sched/sch_dsmark.c index c4c1317cd47d..c955ba24e5cf 100644 --- a/net/sched/sch_dsmark.c +++ b/net/sched/sch_dsmark.c @@ -60,7 +60,8 @@ static int dsmark_graft(struct Qdisc *sch, unsigned long arg, sch, p, new, old); if (new == NULL) { - new = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, + new = qdisc_create_dflt(sch->dev, sch->dev_queue, + &pfifo_qdisc_ops, sch->handle); if (new == NULL) new = &noop_qdisc; @@ -390,7 +391,8 @@ static int dsmark_init(struct Qdisc *sch, struct nlattr *opt) p->default_index = default_index; p->set_tc_index = nla_get_flag(tb[TCA_DSMARK_SET_TC_INDEX]); - p->q = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, sch->handle); + p->q = qdisc_create_dflt(sch->dev, sch->dev_queue, + &pfifo_qdisc_ops, sch->handle); if (p->q == NULL) p->q = &noop_qdisc; diff --git a/net/sched/sch_fifo.c b/net/sched/sch_fifo.c index 82d7d7bbbb16..779eae85faf0 100644 --- a/net/sched/sch_fifo.c +++ b/net/sched/sch_fifo.c @@ -137,7 +137,8 @@ struct Qdisc *fifo_create_dflt(struct Qdisc *sch, struct Qdisc_ops *ops, struct Qdisc *q; int err = -ENOMEM; - q = qdisc_create_dflt(sch->dev, ops, TC_H_MAKE(sch->handle, 1)); + q = qdisc_create_dflt(sch->dev, sch->dev_queue, + ops, TC_H_MAKE(sch->handle, 1)); if (q) { err = fifo_set_limit(q, limit); if (err < 0) { diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 13afa7214392..d97086480893 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -440,7 +440,9 @@ static struct Qdisc_ops pfifo_fast_ops __read_mostly = { .owner = THIS_MODULE, }; -struct Qdisc *qdisc_alloc(struct net_device *dev, struct Qdisc_ops *ops) +struct Qdisc *qdisc_alloc(struct net_device *dev, + struct netdev_queue *dev_queue, + struct Qdisc_ops *ops) { void *p; struct Qdisc *sch; @@ -462,6 +464,7 @@ struct Qdisc *qdisc_alloc(struct net_device *dev, struct Qdisc_ops *ops) sch->ops = ops; sch->enqueue = ops->enqueue; sch->dequeue = ops->dequeue; + sch->dev_queue = dev_queue; sch->dev = dev; dev_hold(dev); atomic_set(&sch->refcnt, 1); @@ -471,12 +474,14 @@ errout: return ERR_PTR(err); } -struct Qdisc * qdisc_create_dflt(struct net_device *dev, struct Qdisc_ops *ops, +struct Qdisc * qdisc_create_dflt(struct net_device *dev, + struct netdev_queue *dev_queue, + struct Qdisc_ops *ops, unsigned int parentid) { struct Qdisc *sch; - sch = qdisc_alloc(dev, ops); + sch = qdisc_alloc(dev, dev_queue, ops); if (IS_ERR(sch)) goto errout; sch->stats_lock = &dev->queue_lock; @@ -545,7 +550,8 @@ void dev_activate(struct net_device *dev) if (dev->qdisc_sleeping == &noop_qdisc) { struct Qdisc *qdisc; if (dev->tx_queue_len) { - qdisc = qdisc_create_dflt(dev, &pfifo_fast_ops, + qdisc = qdisc_create_dflt(dev, &dev->tx_queue, + &pfifo_fast_ops, TC_H_ROOT); if (qdisc == NULL) { printk(KERN_INFO "%s: activation failed\n", dev->name); diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index 3a8267246a4f..5a22fec4eadd 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -1083,7 +1083,8 @@ hfsc_change_class(struct Qdisc *sch, u32 classid, u32 parentid, cl->refcnt = 1; cl->sched = q; cl->cl_parent = parent; - cl->qdisc = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, classid); + cl->qdisc = qdisc_create_dflt(sch->dev, sch->dev_queue, + &pfifo_qdisc_ops, classid); if (cl->qdisc == NULL) cl->qdisc = &noop_qdisc; INIT_LIST_HEAD(&cl->children); @@ -1201,7 +1202,8 @@ hfsc_graft_class(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, if (cl->level > 0) return -EINVAL; if (new == NULL) { - new = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, + new = qdisc_create_dflt(sch->dev, sch->dev_queue, + &pfifo_qdisc_ops, cl->cl_common.classid); if (new == NULL) new = &noop_qdisc; @@ -1443,7 +1445,8 @@ hfsc_init_qdisc(struct Qdisc *sch, struct nlattr *opt) q->root.cl_common.classid = sch->handle; q->root.refcnt = 1; q->root.sched = q; - q->root.qdisc = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, + q->root.qdisc = qdisc_create_dflt(sch->dev, sch->dev_queue, + &pfifo_qdisc_ops, sch->handle); if (q->root.qdisc == NULL) q->root.qdisc = &noop_qdisc; diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index ee8b4ffe110c..956a67f66b9c 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -1129,7 +1129,8 @@ static int htb_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, if (cl && !cl->level) { if (new == NULL && - (new = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, + (new = qdisc_create_dflt(sch->dev, sch->dev_queue, + &pfifo_qdisc_ops, cl->common.classid)) == NULL) return -ENOBUFS; @@ -1256,8 +1257,9 @@ static int htb_delete(struct Qdisc *sch, unsigned long arg) return -EBUSY; if (!cl->level && htb_parent_last_child(cl)) { - new_q = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, - cl->parent->common.classid); + new_q = qdisc_create_dflt(sch->dev, sch->dev_queue, + &pfifo_qdisc_ops, + cl->parent->common.classid); last_child = 1; } @@ -1376,7 +1378,8 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, /* create leaf qdisc early because it uses kmalloc(GFP_KERNEL) so that can't be used inside of sch_tree_lock -- thanks to Karlis Peisenieks */ - new_q = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, classid); + new_q = qdisc_create_dflt(sch->dev, sch->dev_queue, + &pfifo_qdisc_ops, classid); sch_tree_lock(sch); if (parent && !parent->level) { unsigned int qlen = parent->un.leaf.q->q.qlen; diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 24697667247c..aa7a04e32ae9 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -536,7 +536,8 @@ static int netem_init(struct Qdisc *sch, struct nlattr *opt) qdisc_watchdog_init(&q->watchdog, sch); - q->qdisc = qdisc_create_dflt(sch->dev, &tfifo_qdisc_ops, + q->qdisc = qdisc_create_dflt(sch->dev, sch->dev_queue, + &tfifo_qdisc_ops, TC_H_MAKE(sch->handle, 1)); if (!q->qdisc) { pr_debug("netem: qdisc create failed\n"); diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c index 5532f1031ab5..ca58a039208e 100644 --- a/net/sched/sch_prio.c +++ b/net/sched/sch_prio.c @@ -281,7 +281,8 @@ static int prio_tune(struct Qdisc *sch, struct nlattr *opt) for (i=0; ibands; i++) { if (q->queues[i] == &noop_qdisc) { struct Qdisc *child; - child = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, + child = qdisc_create_dflt(sch->dev, sch->dev_queue, + &pfifo_qdisc_ops, TC_H_MAKE(sch->handle, i + 1)); if (child) { sch_tree_lock(sch); -- cgit v1.2.3 From dc2b48475a0a36f8b3bbb2da60d3a006dc5c2c84 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 8 Jul 2008 17:18:23 -0700 Subject: netdev: Move queue_lock into struct netdev_queue. The lock is now an attribute of the device queue. One thing to notice is that "suspicious" places emerge which will need specific training about multiple queue handling. They are so marked with explicit "netdev->rx_queue" and "netdev->tx_queue" references. Signed-off-by: David S. Miller --- drivers/net/ifb.c | 8 ++++---- include/linux/netdevice.h | 4 ++-- net/core/dev.c | 33 ++++++++++++++++++++++----------- net/mac80211/main.c | 10 +++++----- net/mac80211/wme.c | 2 +- net/sched/sch_api.c | 2 +- net/sched/sch_cbq.c | 8 ++++---- net/sched/sch_generic.c | 40 ++++++++++++++++++++-------------------- net/sched/sch_hfsc.c | 4 ++-- net/sched/sch_htb.c | 16 ++++++++-------- net/sched/sch_netem.c | 4 ++-- net/sched/sch_teql.c | 4 ++-- 12 files changed, 73 insertions(+), 62 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c index af233b591534..bc3de272a829 100644 --- a/drivers/net/ifb.c +++ b/drivers/net/ifb.c @@ -229,12 +229,12 @@ module_param(numifbs, int, 0); MODULE_PARM_DESC(numifbs, "Number of ifb devices"); /* - * dev_ifb->queue_lock is usually taken after dev->ingress_lock, + * dev_ifb->tx_queue.lock is usually taken after dev->ingress_lock, * reversely to e.g. qdisc_lock_tree(). It should be safe until - * ifb doesn't take dev->queue_lock with dev_ifb->ingress_lock. + * ifb doesn't take dev->tx_queue.lock with dev_ifb->ingress_lock. * But lockdep should know that ifb has different locks from dev. */ -static struct lock_class_key ifb_queue_lock_key; +static struct lock_class_key ifb_tx_queue_lock_key; static struct lock_class_key ifb_ingress_lock_key; @@ -258,7 +258,7 @@ static int __init ifb_init_one(int index) if (err < 0) goto err; - lockdep_set_class(&dev_ifb->queue_lock, &ifb_queue_lock_key); + lockdep_set_class(&dev_ifb->tx_queue.lock, &ifb_tx_queue_lock_key); lockdep_set_class(&dev_ifb->ingress_lock, &ifb_ingress_lock_key); return 0; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 515fd25bf0fc..e835acacb479 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -449,6 +449,7 @@ static inline void napi_synchronize(const struct napi_struct *n) #endif struct netdev_queue { + spinlock_t lock; struct net_device *dev; }; @@ -629,7 +630,7 @@ struct net_device unsigned char broadcast[MAX_ADDR_LEN]; /* hw bcast add */ struct netdev_queue rx_queue; - struct netdev_queue tx_queue; + struct netdev_queue tx_queue ____cacheline_aligned_in_smp; /* ingress path synchronizer */ spinlock_t ingress_lock; @@ -639,7 +640,6 @@ struct net_device * Cache line mostly used on queue transmit path (qdisc) */ /* device queue lock */ - spinlock_t queue_lock ____cacheline_aligned_in_smp; struct Qdisc *qdisc; struct Qdisc *qdisc_sleeping; struct list_head qdisc_list; diff --git a/net/core/dev.c b/net/core/dev.c index 9b281c906eb0..05011048b86c 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1667,6 +1667,7 @@ out_kfree_skb: int dev_queue_xmit(struct sk_buff *skb) { struct net_device *dev = skb->dev; + struct netdev_queue *txq; struct Qdisc *q; int rc = -ENOMEM; @@ -1699,14 +1700,15 @@ int dev_queue_xmit(struct sk_buff *skb) } gso: - spin_lock_prefetch(&dev->queue_lock); + txq = &dev->tx_queue; + spin_lock_prefetch(&txq->lock); /* Disable soft irqs for various locks below. Also * stops preemption for RCU. */ rcu_read_lock_bh(); - /* Updates of qdisc are serialized by queue_lock. + /* Updates of qdisc are serialized by queue->lock. * The struct Qdisc which is pointed to by qdisc is now a * rcu structure - it may be accessed without acquiring * a lock (but the structure may be stale.) The freeing of the @@ -1714,7 +1716,7 @@ gso: * more references to it. * * If the qdisc has an enqueue function, we still need to - * hold the queue_lock before calling it, since queue_lock + * hold the queue->lock before calling it, since queue->lock * also serializes access to the device queue. */ @@ -1724,19 +1726,19 @@ gso: #endif if (q->enqueue) { /* Grab device queue */ - spin_lock(&dev->queue_lock); + spin_lock(&txq->lock); q = dev->qdisc; if (q->enqueue) { /* reset queue_mapping to zero */ skb_set_queue_mapping(skb, 0); rc = q->enqueue(skb, q); qdisc_run(dev); - spin_unlock(&dev->queue_lock); + spin_unlock(&txq->lock); rc = rc == NET_XMIT_BYPASS ? NET_XMIT_SUCCESS : rc; goto out; } - spin_unlock(&dev->queue_lock); + spin_unlock(&txq->lock); } /* The device has no queue. Common case for software devices: @@ -1919,14 +1921,17 @@ static void net_tx_action(struct softirq_action *h) while (head) { struct net_device *dev = head; + struct netdev_queue *txq; head = head->next_sched; + txq = &dev->tx_queue; + smp_mb__before_clear_bit(); clear_bit(__LINK_STATE_SCHED, &dev->state); - if (spin_trylock(&dev->queue_lock)) { + if (spin_trylock(&txq->lock)) { qdisc_run(dev); - spin_unlock(&dev->queue_lock); + spin_unlock(&txq->lock); } else { netif_schedule(dev); } @@ -3787,7 +3792,6 @@ int register_netdevice(struct net_device *dev) BUG_ON(!dev_net(dev)); net = dev_net(dev); - spin_lock_init(&dev->queue_lock); spin_lock_init(&dev->_xmit_lock); netdev_set_lockdep_class(&dev->_xmit_lock, dev->type); dev->xmit_lock_owner = -1; @@ -4072,10 +4076,17 @@ static struct net_device_stats *internal_stats(struct net_device *dev) return &dev->stats; } +static void netdev_init_one_queue(struct net_device *dev, + struct netdev_queue *queue) +{ + spin_lock_init(&queue->lock); + queue->dev = dev; +} + static void netdev_init_queues(struct net_device *dev) { - dev->rx_queue.dev = dev; - dev->tx_queue.dev = dev; + netdev_init_one_queue(dev, &dev->rx_queue); + netdev_init_one_queue(dev, &dev->tx_queue); } /** diff --git a/net/mac80211/main.c b/net/mac80211/main.c index cf477ad39dac..12aeaf78ae75 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -636,7 +636,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) /* ensure that TX flow won't interrupt us * until the end of the call to requeue function */ - spin_lock_bh(&local->mdev->queue_lock); + spin_lock_bh(&local->mdev->tx_queue.lock); /* create a new queue for this aggregation */ ret = ieee80211_ht_agg_queue_add(local, sta, tid); @@ -675,7 +675,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) /* Will put all the packets in the new SW queue */ ieee80211_requeue(local, ieee802_1d_to_ac[tid]); - spin_unlock_bh(&local->mdev->queue_lock); + spin_unlock_bh(&local->mdev->tx_queue.lock); spin_unlock_bh(&sta->lock); /* send an addBA request */ @@ -701,7 +701,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) err_unlock_queue: kfree(sta->ampdu_mlme.tid_tx[tid]); sta->ampdu_mlme.tid_tx[tid] = NULL; - spin_unlock_bh(&local->mdev->queue_lock); + spin_unlock_bh(&local->mdev->tx_queue.lock); ret = -EBUSY; err_unlock_sta: spin_unlock_bh(&sta->lock); @@ -875,10 +875,10 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid) /* avoid ordering issues: we are the only one that can modify * the content of the qdiscs */ - spin_lock_bh(&local->mdev->queue_lock); + spin_lock_bh(&local->mdev->tx_queue.lock); /* remove the queue for this aggregation */ ieee80211_ht_agg_queue_remove(local, sta, tid, 1); - spin_unlock_bh(&local->mdev->queue_lock); + spin_unlock_bh(&local->mdev->tx_queue.lock); /* we just requeued the all the frames that were in the removed * queue, and since we might miss a softirq we do netif_schedule. diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 2fbc171130bf..59ed9cae66b9 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -648,7 +648,7 @@ int ieee80211_ht_agg_queue_add(struct ieee80211_local *local, } /** - * the caller needs to hold local->mdev->queue_lock + * the caller needs to hold local->mdev->tx_queue.lock */ void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local, struct sta_info *sta, u16 tid, diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 1f893082a4f6..2a1834f8c7d8 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -606,7 +606,7 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue, sch->stats_lock = &dev->ingress_lock; handle = TC_H_MAKE(TC_H_INGRESS, 0); } else { - sch->stats_lock = &dev->queue_lock; + sch->stats_lock = &dev_queue->lock; if (handle == 0) { handle = qdisc_alloc_handle(dev); err = -ENOMEM; diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index 9f2ace585fd6..99ce3da2b0a4 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -1746,10 +1746,10 @@ static void cbq_put(struct Qdisc *sch, unsigned long arg) #ifdef CONFIG_NET_CLS_ACT struct cbq_sched_data *q = qdisc_priv(sch); - spin_lock_bh(&qdisc_dev(sch)->queue_lock); + spin_lock_bh(&sch->dev_queue->lock); if (q->rx_class == cl) q->rx_class = NULL; - spin_unlock_bh(&qdisc_dev(sch)->queue_lock); + spin_unlock_bh(&sch->dev_queue->lock); #endif cbq_destroy_class(sch, cl); @@ -1828,7 +1828,7 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t if (tca[TCA_RATE]) gen_replace_estimator(&cl->bstats, &cl->rate_est, - &qdisc_dev(sch)->queue_lock, + &sch->dev_queue->lock, tca[TCA_RATE]); return 0; } @@ -1919,7 +1919,7 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t if (tca[TCA_RATE]) gen_new_estimator(&cl->bstats, &cl->rate_est, - &qdisc_dev(sch)->queue_lock, tca[TCA_RATE]); + &sch->dev_queue->lock, tca[TCA_RATE]); *arg = (unsigned long)cl; return 0; diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index b626a4f32b6b..ee8f9f78a095 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -29,31 +29,31 @@ /* Main transmission queue. */ /* Modifications to data participating in scheduling must be protected with - * dev->queue_lock spinlock. + * queue->lock spinlock. * * The idea is the following: * - enqueue, dequeue are serialized via top level device - * spinlock dev->queue_lock. + * spinlock queue->lock. * - ingress filtering is serialized via top level device * spinlock dev->ingress_lock. * - updates to tree and tree walking are only done under the rtnl mutex. */ void qdisc_lock_tree(struct net_device *dev) - __acquires(dev->queue_lock) + __acquires(dev->tx_queue.lock) __acquires(dev->ingress_lock) { - spin_lock_bh(&dev->queue_lock); + spin_lock_bh(&dev->tx_queue.lock); spin_lock(&dev->ingress_lock); } EXPORT_SYMBOL(qdisc_lock_tree); void qdisc_unlock_tree(struct net_device *dev) __releases(dev->ingress_lock) - __releases(dev->queue_lock) + __releases(dev->tx_queue.lock) { spin_unlock(&dev->ingress_lock); - spin_unlock_bh(&dev->queue_lock); + spin_unlock_bh(&dev->tx_queue.lock); } EXPORT_SYMBOL(qdisc_unlock_tree); @@ -118,15 +118,15 @@ static inline int handle_dev_cpu_collision(struct sk_buff *skb, } /* - * NOTE: Called under dev->queue_lock with locally disabled BH. + * NOTE: Called under queue->lock with locally disabled BH. * * __LINK_STATE_QDISC_RUNNING guarantees only one CPU can process this - * device at a time. dev->queue_lock serializes queue accesses for + * device at a time. queue->lock serializes queue accesses for * this device AND dev->qdisc pointer itself. * * netif_tx_lock serializes accesses to device driver. * - * dev->queue_lock and netif_tx_lock are mutually exclusive, + * queue->lock and netif_tx_lock are mutually exclusive, * if one is grabbed, another must be free. * * Note, that this procedure can be called by a watchdog timer @@ -148,14 +148,14 @@ static inline int qdisc_restart(struct net_device *dev) /* And release queue */ - spin_unlock(&dev->queue_lock); + spin_unlock(&q->dev_queue->lock); HARD_TX_LOCK(dev, smp_processor_id()); if (!netif_subqueue_stopped(dev, skb)) ret = dev_hard_start_xmit(skb, dev); HARD_TX_UNLOCK(dev); - spin_lock(&dev->queue_lock); + spin_lock(&q->dev_queue->lock); q = dev->qdisc; switch (ret) { @@ -482,7 +482,7 @@ struct Qdisc * qdisc_create_dflt(struct net_device *dev, sch = qdisc_alloc(dev_queue, ops); if (IS_ERR(sch)) goto errout; - sch->stats_lock = &dev->queue_lock; + sch->stats_lock = &dev_queue->lock; sch->parent = parentid; if (!ops->init || ops->init(sch, NULL) == 0) @@ -494,7 +494,7 @@ errout: } EXPORT_SYMBOL(qdisc_create_dflt); -/* Under dev->queue_lock and BH! */ +/* Under queue->lock and BH! */ void qdisc_reset(struct Qdisc *qdisc) { @@ -514,7 +514,7 @@ static void __qdisc_destroy(struct rcu_head *head) kfree((char *) qdisc - qdisc->padded); } -/* Under dev->queue_lock and BH! */ +/* Under queue->lock and BH! */ void qdisc_destroy(struct Qdisc *qdisc) { @@ -566,13 +566,13 @@ void dev_activate(struct net_device *dev) /* Delay activation until next carrier-on event */ return; - spin_lock_bh(&dev->queue_lock); + spin_lock_bh(&dev->tx_queue.lock); rcu_assign_pointer(dev->qdisc, dev->qdisc_sleeping); if (dev->qdisc != &noqueue_qdisc) { dev->trans_start = jiffies; dev_watchdog_up(dev); } - spin_unlock_bh(&dev->queue_lock); + spin_unlock_bh(&dev->tx_queue.lock); } void dev_deactivate(struct net_device *dev) @@ -581,7 +581,7 @@ void dev_deactivate(struct net_device *dev) struct sk_buff *skb; int running; - spin_lock_bh(&dev->queue_lock); + spin_lock_bh(&dev->tx_queue.lock); qdisc = dev->qdisc; dev->qdisc = &noop_qdisc; @@ -589,7 +589,7 @@ void dev_deactivate(struct net_device *dev) skb = dev->gso_skb; dev->gso_skb = NULL; - spin_unlock_bh(&dev->queue_lock); + spin_unlock_bh(&dev->tx_queue.lock); kfree_skb(skb); @@ -607,9 +607,9 @@ void dev_deactivate(struct net_device *dev) * Double-check inside queue lock to ensure that all effects * of the queue run are visible when we return. */ - spin_lock_bh(&dev->queue_lock); + spin_lock_bh(&dev->tx_queue.lock); running = test_bit(__LINK_STATE_QDISC_RUNNING, &dev->state); - spin_unlock_bh(&dev->queue_lock); + spin_unlock_bh(&dev->tx_queue.lock); /* * The running flag should never be set at this point because diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index 333525422f45..997d520ca580 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -1045,7 +1045,7 @@ hfsc_change_class(struct Qdisc *sch, u32 classid, u32 parentid, if (tca[TCA_RATE]) gen_replace_estimator(&cl->bstats, &cl->rate_est, - &qdisc_dev(sch)->queue_lock, + &sch->dev_queue->lock, tca[TCA_RATE]); return 0; } @@ -1104,7 +1104,7 @@ hfsc_change_class(struct Qdisc *sch, u32 classid, u32 parentid, if (tca[TCA_RATE]) gen_new_estimator(&cl->bstats, &cl->rate_est, - &qdisc_dev(sch)->queue_lock, tca[TCA_RATE]); + &sch->dev_queue->lock, tca[TCA_RATE]); *arg = (unsigned long)cl; return 0; } diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 31f7d1536e6d..c8ca54cc26b0 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -1043,7 +1043,7 @@ static int htb_dump(struct Qdisc *sch, struct sk_buff *skb) struct nlattr *nest; struct tc_htb_glob gopt; - spin_lock_bh(&qdisc_dev(sch)->queue_lock); + spin_lock_bh(&sch->dev_queue->lock); gopt.direct_pkts = q->direct_pkts; gopt.version = HTB_VER; @@ -1057,11 +1057,11 @@ static int htb_dump(struct Qdisc *sch, struct sk_buff *skb) NLA_PUT(skb, TCA_HTB_INIT, sizeof(gopt), &gopt); nla_nest_end(skb, nest); - spin_unlock_bh(&qdisc_dev(sch)->queue_lock); + spin_unlock_bh(&sch->dev_queue->lock); return skb->len; nla_put_failure: - spin_unlock_bh(&qdisc_dev(sch)->queue_lock); + spin_unlock_bh(&sch->dev_queue->lock); nla_nest_cancel(skb, nest); return -1; } @@ -1073,7 +1073,7 @@ static int htb_dump_class(struct Qdisc *sch, unsigned long arg, struct nlattr *nest; struct tc_htb_opt opt; - spin_lock_bh(&qdisc_dev(sch)->queue_lock); + spin_lock_bh(&sch->dev_queue->lock); tcm->tcm_parent = cl->parent ? cl->parent->common.classid : TC_H_ROOT; tcm->tcm_handle = cl->common.classid; if (!cl->level && cl->un.leaf.q) @@ -1095,11 +1095,11 @@ static int htb_dump_class(struct Qdisc *sch, unsigned long arg, NLA_PUT(skb, TCA_HTB_PARMS, sizeof(opt), &opt); nla_nest_end(skb, nest); - spin_unlock_bh(&qdisc_dev(sch)->queue_lock); + spin_unlock_bh(&sch->dev_queue->lock); return skb->len; nla_put_failure: - spin_unlock_bh(&qdisc_dev(sch)->queue_lock); + spin_unlock_bh(&sch->dev_queue->lock); nla_nest_cancel(skb, nest); return -1; } @@ -1365,7 +1365,7 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, goto failure; gen_new_estimator(&cl->bstats, &cl->rate_est, - &qdisc_dev(sch)->queue_lock, + &sch->dev_queue->lock, tca[TCA_RATE] ? : &est.nla); cl->refcnt = 1; cl->children = 0; @@ -1420,7 +1420,7 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, } else { if (tca[TCA_RATE]) gen_replace_estimator(&cl->bstats, &cl->rate_est, - &qdisc_dev(sch)->queue_lock, + &sch->dev_queue->lock, tca[TCA_RATE]); sch_tree_lock(sch); } diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 790582960444..71b73c528f9b 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -333,9 +333,9 @@ static int get_dist_table(struct Qdisc *sch, const struct nlattr *attr) for (i = 0; i < n; i++) d->table[i] = data[i]; - spin_lock_bh(&qdisc_dev(sch)->queue_lock); + spin_lock_bh(&sch->dev_queue->lock); d = xchg(&q->delay_dist, d); - spin_unlock_bh(&qdisc_dev(sch)->queue_lock); + spin_unlock_bh(&sch->dev_queue->lock); kfree(d); return 0; diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index b3fc82623fc6..4f3054e8e1ab 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -154,9 +154,9 @@ teql_destroy(struct Qdisc* sch) master->slaves = NEXT_SLAVE(q); if (q == master->slaves) { master->slaves = NULL; - spin_lock_bh(&master->dev->queue_lock); + spin_lock_bh(&master->dev->tx_queue.lock); qdisc_reset(master->dev->qdisc); - spin_unlock_bh(&master->dev->queue_lock); + spin_unlock_bh(&master->dev->tx_queue.lock); } } skb_queue_purge(&dat->q); -- cgit v1.2.3 From 555353cfa1aee293de445bfa6de43276138ddd82 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 8 Jul 2008 17:33:13 -0700 Subject: netdev: The ingress_lock member is no longer needed. Every qdisc is assosciated with a queue, and in the case of ingress qdiscs that will now be netdev->rx_queue so using that queue's lock is the thing to do. Signed-off-by: David S. Miller --- drivers/net/ifb.c | 8 ++++---- include/linux/netdevice.h | 2 -- net/core/dev.c | 12 +++++++----- net/sched/sch_api.c | 3 +-- net/sched/sch_generic.c | 10 +++++----- 5 files changed, 17 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c index bc3de272a829..ccbd6554f6eb 100644 --- a/drivers/net/ifb.c +++ b/drivers/net/ifb.c @@ -229,13 +229,13 @@ module_param(numifbs, int, 0); MODULE_PARM_DESC(numifbs, "Number of ifb devices"); /* - * dev_ifb->tx_queue.lock is usually taken after dev->ingress_lock, + * dev_ifb->tx_queue.lock is usually taken after dev->rx_queue.lock, * reversely to e.g. qdisc_lock_tree(). It should be safe until - * ifb doesn't take dev->tx_queue.lock with dev_ifb->ingress_lock. + * ifb doesn't take dev->tx_queue.lock with dev_ifb->rx_queue.lock. * But lockdep should know that ifb has different locks from dev. */ static struct lock_class_key ifb_tx_queue_lock_key; -static struct lock_class_key ifb_ingress_lock_key; +static struct lock_class_key ifb_rx_queue_lock_key; static int __init ifb_init_one(int index) @@ -259,7 +259,7 @@ static int __init ifb_init_one(int index) goto err; lockdep_set_class(&dev_ifb->tx_queue.lock, &ifb_tx_queue_lock_key); - lockdep_set_class(&dev_ifb->ingress_lock, &ifb_ingress_lock_key); + lockdep_set_class(&dev_ifb->rx_queue.lock, &ifb_rx_queue_lock_key); return 0; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index e835acacb479..633a44c6fa5e 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -632,8 +632,6 @@ struct net_device struct netdev_queue rx_queue; struct netdev_queue tx_queue ____cacheline_aligned_in_smp; - /* ingress path synchronizer */ - spinlock_t ingress_lock; struct Qdisc *qdisc_ingress; /* diff --git a/net/core/dev.c b/net/core/dev.c index 05011048b86c..2322fb69fd53 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2014,10 +2014,11 @@ static inline struct sk_buff *handle_macvlan(struct sk_buff *skb, */ static int ing_filter(struct sk_buff *skb) { - struct Qdisc *q; struct net_device *dev = skb->dev; - int result = TC_ACT_OK; u32 ttl = G_TC_RTTL(skb->tc_verd); + struct netdev_queue *rxq; + int result = TC_ACT_OK; + struct Qdisc *q; if (MAX_RED_LOOP < ttl++) { printk(KERN_WARNING @@ -2029,10 +2030,12 @@ static int ing_filter(struct sk_buff *skb) skb->tc_verd = SET_TC_RTTL(skb->tc_verd, ttl); skb->tc_verd = SET_TC_AT(skb->tc_verd, AT_INGRESS); - spin_lock(&dev->ingress_lock); + rxq = &dev->rx_queue; + + spin_lock(&rxq->lock); if ((q = dev->qdisc_ingress) != NULL) result = q->enqueue(skb, q); - spin_unlock(&dev->ingress_lock); + spin_unlock(&rxq->lock); return result; } @@ -3795,7 +3798,6 @@ int register_netdevice(struct net_device *dev) spin_lock_init(&dev->_xmit_lock); netdev_set_lockdep_class(&dev->_xmit_lock, dev->type); dev->xmit_lock_owner = -1; - spin_lock_init(&dev->ingress_lock); dev->iflink = -1; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 2a1834f8c7d8..570cef2a9c5f 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -601,12 +601,11 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue, sch->parent = parent; + sch->stats_lock = &dev_queue->lock; if (handle == TC_H_INGRESS) { sch->flags |= TCQ_F_INGRESS; - sch->stats_lock = &dev->ingress_lock; handle = TC_H_MAKE(TC_H_INGRESS, 0); } else { - sch->stats_lock = &dev_queue->lock; if (handle == 0) { handle = qdisc_alloc_handle(dev); err = -ENOMEM; diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index ee8f9f78a095..804d44b00348 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -35,24 +35,24 @@ * - enqueue, dequeue are serialized via top level device * spinlock queue->lock. * - ingress filtering is serialized via top level device - * spinlock dev->ingress_lock. + * spinlock dev->rx_queue.lock. * - updates to tree and tree walking are only done under the rtnl mutex. */ void qdisc_lock_tree(struct net_device *dev) __acquires(dev->tx_queue.lock) - __acquires(dev->ingress_lock) + __acquires(dev->rx_queue.lock) { spin_lock_bh(&dev->tx_queue.lock); - spin_lock(&dev->ingress_lock); + spin_lock(&dev->rx_queue.lock); } EXPORT_SYMBOL(qdisc_lock_tree); void qdisc_unlock_tree(struct net_device *dev) - __releases(dev->ingress_lock) + __releases(dev->rx_queue.lock) __releases(dev->tx_queue.lock) { - spin_unlock(&dev->ingress_lock); + spin_unlock(&dev->rx_queue.lock); spin_unlock_bh(&dev->tx_queue.lock); } EXPORT_SYMBOL(qdisc_unlock_tree); -- cgit v1.2.3 From b0e1e6462df3c5944010b3328a546d8fe5d932cd Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 8 Jul 2008 17:42:10 -0700 Subject: netdev: Move rest of qdisc state into struct netdev_queue Now qdisc, qdisc_sleeping, and qdisc_list also live there. Signed-off-by: David S. Miller --- drivers/isdn/i4l/isdn_net.c | 2 +- include/linux/netdevice.h | 10 ++--- include/net/irda/irda_device.h | 2 +- net/core/dev.c | 4 +- net/core/link_watch.c | 8 +++- net/core/rtnetlink.c | 6 ++- net/ipv6/addrconf.c | 3 +- net/mac80211/wme.c | 20 ++++++---- net/sched/cls_api.c | 7 +++- net/sched/sch_api.c | 34 ++++++++++------ net/sched/sch_generic.c | 90 ++++++++++++++++++++++++++---------------- net/sched/sch_netem.c | 2 +- net/sched/sch_teql.c | 14 ++++--- 13 files changed, 125 insertions(+), 77 deletions(-) (limited to 'include/linux') diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c index ef1a300068dc..457bbd119f9b 100644 --- a/drivers/isdn/i4l/isdn_net.c +++ b/drivers/isdn/i4l/isdn_net.c @@ -287,7 +287,7 @@ isdn_net_unbind_channel(isdn_net_local * lp) BEWARE! This chunk of code cannot be called from hardware interrupt handler. I hope it is true. --ANK */ - qdisc_reset(lp->netdev->dev->qdisc); + qdisc_reset(lp->netdev->dev->tx_queue.qdisc); } lp->dialstate = 0; dev->rx_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 633a44c6fa5e..df702a7b3db5 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -451,6 +451,9 @@ static inline void napi_synchronize(const struct napi_struct *n) struct netdev_queue { spinlock_t lock; struct net_device *dev; + struct Qdisc *qdisc; + struct Qdisc *qdisc_sleeping; + struct list_head qdisc_list; }; /* @@ -634,13 +637,6 @@ struct net_device struct Qdisc *qdisc_ingress; -/* - * Cache line mostly used on queue transmit path (qdisc) - */ - /* device queue lock */ - struct Qdisc *qdisc; - struct Qdisc *qdisc_sleeping; - struct list_head qdisc_list; unsigned long tx_queue_len; /* Max frames per queue allowed */ /* Partially transmitted GSO packet. */ diff --git a/include/net/irda/irda_device.h b/include/net/irda/irda_device.h index f70e9b39ebaf..16fbf672e0b2 100644 --- a/include/net/irda/irda_device.h +++ b/include/net/irda/irda_device.h @@ -223,7 +223,7 @@ int irda_device_is_receiving(struct net_device *dev); /* Interface for internal use */ static inline int irda_device_txqueue_empty(const struct net_device *dev) { - return skb_queue_empty(&dev->qdisc->q); + return skb_queue_empty(&dev->tx_queue.qdisc->q); } int irda_device_set_raw_mode(struct net_device* self, int status); struct net_device *alloc_irdadev(int sizeof_priv); diff --git a/net/core/dev.c b/net/core/dev.c index 2322fb69fd53..ce79c28d739d 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1720,14 +1720,14 @@ gso: * also serializes access to the device queue. */ - q = rcu_dereference(dev->qdisc); + q = rcu_dereference(txq->qdisc); #ifdef CONFIG_NET_CLS_ACT skb->tc_verd = SET_TC_AT(skb->tc_verd,AT_EGRESS); #endif if (q->enqueue) { /* Grab device queue */ spin_lock(&txq->lock); - q = dev->qdisc; + q = txq->qdisc; if (q->enqueue) { /* reset queue_mapping to zero */ skb_set_queue_mapping(skb, 0); diff --git a/net/core/link_watch.c b/net/core/link_watch.c index a5e372b9ec4d..50218218445b 100644 --- a/net/core/link_watch.c +++ b/net/core/link_watch.c @@ -79,8 +79,10 @@ static void rfc2863_policy(struct net_device *dev) static int linkwatch_urgent_event(struct net_device *dev) { + struct netdev_queue *txq = &dev->tx_queue; + return netif_running(dev) && netif_carrier_ok(dev) && - dev->qdisc != dev->qdisc_sleeping; + txq->qdisc != txq->qdisc_sleeping; } @@ -181,7 +183,9 @@ static void __linkwatch_run_queue(int urgent_only) rfc2863_policy(dev); if (dev->flags & IFF_UP) { if (netif_carrier_ok(dev)) { - WARN_ON(dev->qdisc_sleeping == &noop_qdisc); + struct netdev_queue *txq = &dev->tx_queue; + + WARN_ON(txq->qdisc_sleeping == &noop_qdisc); dev_activate(dev); } else dev_deactivate(dev); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 6c8d7f0ea01a..8ef9f1db610e 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -605,6 +605,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, int type, u32 pid, u32 seq, u32 change, unsigned int flags) { + struct netdev_queue *txq; struct ifinfomsg *ifm; struct nlmsghdr *nlh; struct net_device_stats *stats; @@ -635,8 +636,9 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, if (dev->master) NLA_PUT_U32(skb, IFLA_MASTER, dev->master->ifindex); - if (dev->qdisc_sleeping) - NLA_PUT_STRING(skb, IFLA_QDISC, dev->qdisc_sleeping->ops->id); + txq = &dev->tx_queue; + if (txq->qdisc_sleeping) + NLA_PUT_STRING(skb, IFLA_QDISC, txq->qdisc_sleeping->ops->id); if (1) { struct rtnl_link_ifmap map = { diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 8572cb05fc21..5c84c798331d 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -231,7 +231,8 @@ const struct in6_addr in6addr_linklocal_allrouters = IN6ADDR_LINKLOCAL_ALLROUTER /* Check if a valid qdisc is available */ static inline int addrconf_qdisc_ok(struct net_device *dev) { - return (dev->qdisc != &noop_qdisc); + struct netdev_queue *txq = &dev->tx_queue; + return (txq->qdisc != &noop_qdisc); } /* Check if a route is valid prefix route */ diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 59ed9cae66b9..6ae43a3c7726 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -574,9 +574,10 @@ static struct Qdisc_ops wme_qdisc_ops __read_mostly = void ieee80211_install_qdisc(struct net_device *dev) { + struct netdev_queue *txq = &dev->tx_queue; struct Qdisc *qdisc; - qdisc = qdisc_create_dflt(dev, &dev->tx_queue, + qdisc = qdisc_create_dflt(dev, txq, &wme_qdisc_ops, TC_H_ROOT); if (!qdisc) { printk(KERN_ERR "%s: qdisc installation failed\n", dev->name); @@ -587,15 +588,17 @@ void ieee80211_install_qdisc(struct net_device *dev) qdisc->handle = 0x80010000; qdisc_lock_tree(dev); - list_add_tail(&qdisc->list, &dev->qdisc_list); - dev->qdisc_sleeping = qdisc; + list_add_tail(&qdisc->list, &txq->qdisc_list); + txq->qdisc_sleeping = qdisc; qdisc_unlock_tree(dev); } int ieee80211_qdisc_installed(struct net_device *dev) { - return dev->qdisc_sleeping->ops == &wme_qdisc_ops; + struct netdev_queue *txq = &dev->tx_queue; + + return txq->qdisc_sleeping->ops == &wme_qdisc_ops; } @@ -614,8 +617,9 @@ int ieee80211_ht_agg_queue_add(struct ieee80211_local *local, struct sta_info *sta, u16 tid) { int i; + struct netdev_queue *txq = &local->mdev->tx_queue; struct ieee80211_sched_data *q = - qdisc_priv(local->mdev->qdisc_sleeping); + qdisc_priv(txq->qdisc_sleeping); DECLARE_MAC_BUF(mac); /* prepare the filter and save it for the SW queue @@ -655,8 +659,9 @@ void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local, u8 requeue) { struct ieee80211_hw *hw = &local->hw; + struct netdev_queue *txq = &local->mdev->tx_queue; struct ieee80211_sched_data *q = - qdisc_priv(local->mdev->qdisc_sleeping); + qdisc_priv(txq->qdisc_sleeping); int agg_queue = sta->tid_to_tx_q[tid]; /* return the qdisc to the pool */ @@ -671,7 +676,8 @@ void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local, void ieee80211_requeue(struct ieee80211_local *local, int queue) { - struct Qdisc *root_qd = local->mdev->qdisc_sleeping; + struct netdev_queue *txq = &local->mdev->tx_queue; + struct Qdisc *root_qd = txq->qdisc_sleeping; struct ieee80211_sched_data *q = qdisc_priv(root_qd); struct Qdisc *qdisc = q->queues[queue]; struct sk_buff *skb = NULL; diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index e2389f161e46..b483bbea6118 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -166,7 +166,8 @@ replay: /* Find qdisc */ if (!parent) { - q = dev->qdisc_sleeping; + struct netdev_queue *dev_queue = &dev->tx_queue; + q = dev_queue->qdisc_sleeping; parent = q->handle; } else { q = qdisc_lookup(dev, TC_H_MAJ(t->tcm_parent)); @@ -390,6 +391,7 @@ static int tcf_node_dump(struct tcf_proto *tp, unsigned long n, static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb) { struct net *net = sock_net(skb->sk); + struct netdev_queue *dev_queue; int t; int s_t; struct net_device *dev; @@ -408,8 +410,9 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb) if ((dev = dev_get_by_index(&init_net, tcm->tcm_ifindex)) == NULL) return skb->len; + dev_queue = &dev->tx_queue; if (!tcm->tcm_parent) - q = dev->qdisc_sleeping; + q = dev_queue->qdisc_sleeping; else q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent)); if (!q) diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 570cef2a9c5f..2313fa7c97be 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -185,9 +185,10 @@ EXPORT_SYMBOL(unregister_qdisc); struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle) { + struct netdev_queue *dev_queue = &dev->tx_queue; struct Qdisc *q; - list_for_each_entry(q, &dev->qdisc_list, list) { + list_for_each_entry(q, &dev_queue->qdisc_list, list) { if (q->handle == handle) return q; } @@ -441,6 +442,7 @@ static u32 qdisc_alloc_handle(struct net_device *dev) static struct Qdisc * dev_graft_qdisc(struct net_device *dev, struct Qdisc *qdisc) { + struct netdev_queue *dev_queue; struct Qdisc *oqdisc; if (dev->flags & IFF_UP) @@ -459,8 +461,8 @@ dev_graft_qdisc(struct net_device *dev, struct Qdisc *qdisc) } } else { - - oqdisc = dev->qdisc_sleeping; + dev_queue = &dev->tx_queue; + oqdisc = dev_queue->qdisc_sleeping; /* Prune old scheduler */ if (oqdisc && atomic_read(&oqdisc->refcnt) <= 1) @@ -469,8 +471,8 @@ dev_graft_qdisc(struct net_device *dev, struct Qdisc *qdisc) /* ... and graft new one */ if (qdisc == NULL) qdisc = &noop_qdisc; - dev->qdisc_sleeping = qdisc; - dev->qdisc = &noop_qdisc; + dev_queue->qdisc_sleeping = qdisc; + dev_queue->qdisc = &noop_qdisc; } qdisc_unlock_tree(dev); @@ -633,7 +635,7 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue, } } qdisc_lock_tree(dev); - list_add_tail(&sch->list, &dev->qdisc_list); + list_add_tail(&sch->list, &dev_queue->qdisc_list); qdisc_unlock_tree(dev); return sch; @@ -740,7 +742,8 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg) q = dev->qdisc_ingress; } } else { - q = dev->qdisc_sleeping; + struct netdev_queue *dev_queue = &dev->tx_queue; + q = dev_queue->qdisc_sleeping; } if (!q) return -ENOENT; @@ -814,7 +817,8 @@ replay: q = dev->qdisc_ingress; } } else { - q = dev->qdisc_sleeping; + struct netdev_queue *dev_queue = &dev->tx_queue; + q = dev_queue->qdisc_sleeping; } /* It may be default qdisc, ignore it */ @@ -1015,12 +1019,14 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb) read_lock(&dev_base_lock); idx = 0; for_each_netdev(&init_net, dev) { + struct netdev_queue *dev_queue; if (idx < s_idx) goto cont; if (idx > s_idx) s_q_idx = 0; q_idx = 0; - list_for_each_entry(q, &dev->qdisc_list, list) { + dev_queue = &dev->tx_queue; + list_for_each_entry(q, &dev_queue->qdisc_list, list) { if (q_idx < s_q_idx) { q_idx++; continue; @@ -1054,6 +1060,7 @@ done: static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg) { struct net *net = sock_net(skb->sk); + struct netdev_queue *dev_queue; struct tcmsg *tcm = NLMSG_DATA(n); struct nlattr *tca[TCA_MAX + 1]; struct net_device *dev; @@ -1091,6 +1098,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg) /* Step 1. Determine qdisc handle X:0 */ + dev_queue = &dev->tx_queue; if (pid != TC_H_ROOT) { u32 qid1 = TC_H_MAJ(pid); @@ -1101,7 +1109,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg) } else if (qid1) { qid = qid1; } else if (qid == 0) - qid = dev->qdisc_sleeping->handle; + qid = dev_queue->qdisc_sleeping->handle; /* Now qid is genuine qdisc handle consistent both with parent and child. @@ -1112,7 +1120,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg) pid = TC_H_MAKE(qid, pid); } else { if (qid == 0) - qid = dev->qdisc_sleeping->handle; + qid = dev_queue->qdisc_sleeping->handle; } /* OK. Locate qdisc */ @@ -1248,6 +1256,7 @@ static int qdisc_class_dump(struct Qdisc *q, unsigned long cl, struct qdisc_walk static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb) { struct net *net = sock_net(skb->sk); + struct netdev_queue *dev_queue; int t; int s_t; struct net_device *dev; @@ -1266,7 +1275,8 @@ static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb) s_t = cb->args[0]; t = 0; - list_for_each_entry(q, &dev->qdisc_list, list) { + dev_queue = &dev->tx_queue; + list_for_each_entry(q, &dev_queue->qdisc_list, list) { if (t < s_t || !q->ops->cl_ops || (tcm->tcm_parent && TC_H_MAJ(tcm->tcm_parent) != q->handle)) { diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 804d44b00348..3223e5ba76aa 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -122,7 +122,7 @@ static inline int handle_dev_cpu_collision(struct sk_buff *skb, * * __LINK_STATE_QDISC_RUNNING guarantees only one CPU can process this * device at a time. queue->lock serializes queue accesses for - * this device AND dev->qdisc pointer itself. + * this device AND txq->qdisc pointer itself. * * netif_tx_lock serializes accesses to device driver. * @@ -138,7 +138,8 @@ static inline int handle_dev_cpu_collision(struct sk_buff *skb, */ static inline int qdisc_restart(struct net_device *dev) { - struct Qdisc *q = dev->qdisc; + struct netdev_queue *txq = &dev->tx_queue; + struct Qdisc *q = txq->qdisc; struct sk_buff *skb; int ret = NETDEV_TX_BUSY; @@ -148,15 +149,15 @@ static inline int qdisc_restart(struct net_device *dev) /* And release queue */ - spin_unlock(&q->dev_queue->lock); + spin_unlock(&txq->lock); HARD_TX_LOCK(dev, smp_processor_id()); if (!netif_subqueue_stopped(dev, skb)) ret = dev_hard_start_xmit(skb, dev); HARD_TX_UNLOCK(dev); - spin_lock(&q->dev_queue->lock); - q = dev->qdisc; + spin_lock(&txq->lock); + q = txq->qdisc; switch (ret) { case NETDEV_TX_OK: @@ -207,9 +208,10 @@ void __qdisc_run(struct net_device *dev) static void dev_watchdog(unsigned long arg) { struct net_device *dev = (struct net_device *)arg; + struct netdev_queue *txq = &dev->tx_queue; netif_tx_lock(dev); - if (dev->qdisc != &noop_qdisc) { + if (txq->qdisc != &noop_qdisc) { if (netif_device_present(dev) && netif_running(dev) && netif_carrier_ok(dev)) { @@ -539,53 +541,63 @@ EXPORT_SYMBOL(qdisc_destroy); void dev_activate(struct net_device *dev) { + struct netdev_queue *txq = &dev->tx_queue; + /* No queueing discipline is attached to device; create default one i.e. pfifo_fast for devices, which need queueing and noqueue_qdisc for virtual interfaces */ - if (dev->qdisc_sleeping == &noop_qdisc) { + if (txq->qdisc_sleeping == &noop_qdisc) { struct Qdisc *qdisc; if (dev->tx_queue_len) { - qdisc = qdisc_create_dflt(dev, &dev->tx_queue, + qdisc = qdisc_create_dflt(dev, txq, &pfifo_fast_ops, TC_H_ROOT); if (qdisc == NULL) { printk(KERN_INFO "%s: activation failed\n", dev->name); return; } - list_add_tail(&qdisc->list, &dev->qdisc_list); + list_add_tail(&qdisc->list, &txq->qdisc_list); } else { qdisc = &noqueue_qdisc; } - dev->qdisc_sleeping = qdisc; + txq->qdisc_sleeping = qdisc; } if (!netif_carrier_ok(dev)) /* Delay activation until next carrier-on event */ return; - spin_lock_bh(&dev->tx_queue.lock); - rcu_assign_pointer(dev->qdisc, dev->qdisc_sleeping); - if (dev->qdisc != &noqueue_qdisc) { + spin_lock_bh(&txq->lock); + rcu_assign_pointer(txq->qdisc, txq->qdisc_sleeping); + if (txq->qdisc != &noqueue_qdisc) { dev->trans_start = jiffies; dev_watchdog_up(dev); } - spin_unlock_bh(&dev->tx_queue.lock); + spin_unlock_bh(&txq->lock); +} + +static void dev_deactivate_queue(struct net_device *dev, + struct netdev_queue *dev_queue, + struct Qdisc *qdisc_default) +{ + struct Qdisc *qdisc = dev_queue->qdisc; + + if (qdisc) { + dev_queue->qdisc = qdisc_default; + qdisc_reset(qdisc); + } } void dev_deactivate(struct net_device *dev) { - struct Qdisc *qdisc; struct sk_buff *skb; int running; spin_lock_bh(&dev->tx_queue.lock); - qdisc = dev->qdisc; - dev->qdisc = &noop_qdisc; - - qdisc_reset(qdisc); + dev_deactivate_queue(dev, &dev->tx_queue, &noop_qdisc); skb = dev->gso_skb; dev->gso_skb = NULL; @@ -622,32 +634,44 @@ void dev_deactivate(struct net_device *dev) } while (WARN_ON_ONCE(running)); } +static void dev_init_scheduler_queue(struct net_device *dev, + struct netdev_queue *dev_queue, + struct Qdisc *qdisc) +{ + dev_queue->qdisc = qdisc; + dev_queue->qdisc_sleeping = qdisc; + INIT_LIST_HEAD(&dev_queue->qdisc_list); +} + void dev_init_scheduler(struct net_device *dev) { qdisc_lock_tree(dev); - dev->qdisc = &noop_qdisc; - dev->qdisc_sleeping = &noop_qdisc; - INIT_LIST_HEAD(&dev->qdisc_list); + dev_init_scheduler_queue(dev, &dev->tx_queue, &noop_qdisc); + dev_init_scheduler_queue(dev, &dev->rx_queue, NULL); qdisc_unlock_tree(dev); setup_timer(&dev->watchdog_timer, dev_watchdog, (unsigned long)dev); } -void dev_shutdown(struct net_device *dev) +static void dev_shutdown_scheduler_queue(struct net_device *dev, + struct netdev_queue *dev_queue, + struct Qdisc *qdisc_default) { - struct Qdisc *qdisc; + struct Qdisc *qdisc = dev_queue->qdisc_sleeping; + + if (qdisc) { + dev_queue->qdisc = qdisc_default; + dev_queue->qdisc_sleeping = qdisc_default; - qdisc_lock_tree(dev); - qdisc = dev->qdisc_sleeping; - dev->qdisc = &noop_qdisc; - dev->qdisc_sleeping = &noop_qdisc; - qdisc_destroy(qdisc); -#if defined(CONFIG_NET_SCH_INGRESS) || defined(CONFIG_NET_SCH_INGRESS_MODULE) - if ((qdisc = dev->qdisc_ingress) != NULL) { - dev->qdisc_ingress = NULL; qdisc_destroy(qdisc); } -#endif +} + +void dev_shutdown(struct net_device *dev) +{ + qdisc_lock_tree(dev); + dev_shutdown_scheduler_queue(dev, &dev->tx_queue, &noop_qdisc); + dev_shutdown_scheduler_queue(dev, &dev->rx_queue, NULL); BUG_TRAP(!timer_pending(&dev->watchdog_timer)); qdisc_unlock_tree(dev); } diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 71b73c528f9b..4093f1eaaf60 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -180,7 +180,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch) * skb will be queued. */ if (count > 1 && (skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) { - struct Qdisc *rootq = qdisc_dev(sch)->qdisc; + struct Qdisc *rootq = qdisc_dev(sch)->tx_queue.qdisc; u32 dupsave = q->duplicate; /* prevent duplicating a dup... */ q->duplicate = 0; diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index 4f3054e8e1ab..8ac05981be20 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -107,17 +107,19 @@ static struct sk_buff * teql_dequeue(struct Qdisc* sch) { struct teql_sched_data *dat = qdisc_priv(sch); + struct netdev_queue *dat_queue; struct sk_buff *skb; skb = __skb_dequeue(&dat->q); + dat_queue = &dat->m->dev->tx_queue; if (skb == NULL) { - struct net_device *m = qdisc_dev(dat->m->dev->qdisc); + struct net_device *m = qdisc_dev(dat_queue->qdisc); if (m) { dat->m->slaves = sch; netif_wake_queue(m); } } - sch->q.qlen = dat->q.qlen + dat->m->dev->qdisc->q.qlen; + sch->q.qlen = dat->q.qlen + dat_queue->qdisc->q.qlen; return skb; } @@ -155,7 +157,7 @@ teql_destroy(struct Qdisc* sch) if (q == master->slaves) { master->slaves = NULL; spin_lock_bh(&master->dev->tx_queue.lock); - qdisc_reset(master->dev->qdisc); + qdisc_reset(master->dev->tx_queue.qdisc); spin_unlock_bh(&master->dev->tx_queue.lock); } } @@ -216,7 +218,7 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt) static int __teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res, struct net_device *dev) { - struct teql_sched_data *q = qdisc_priv(dev->qdisc); + struct teql_sched_data *q = qdisc_priv(dev->tx_queue.qdisc); struct neighbour *mn = skb->dst->neighbour; struct neighbour *n = q->ncache; @@ -252,7 +254,7 @@ __teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res, struct net_device * static inline int teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res, struct net_device *dev) { - if (dev->qdisc == &noop_qdisc) + if (dev->tx_queue.qdisc == &noop_qdisc) return -ENODEV; if (dev->header_ops == NULL || @@ -284,7 +286,7 @@ restart: do { struct net_device *slave = qdisc_dev(q); - if (slave->qdisc_sleeping != q) + if (slave->tx_queue.qdisc_sleeping != q) continue; if (netif_queue_stopped(slave) || __netif_subqueue_stopped(slave, subq) || -- cgit v1.2.3 From 816f3258e70db38d6d92c8d871377179fd69160f Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 8 Jul 2008 22:49:00 -0700 Subject: netdev: Kill qdisc_ingress, use netdev->rx_queue.qdisc instead. Now that our qdisc management is bi-directional, per-queue, and fully orthogonal, there is no reason to have a special ingress qdisc pointer in struct net_device. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 3 --- net/core/dev.c | 4 ++-- net/sched/sch_api.c | 11 ++++++----- 3 files changed, 8 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index df702a7b3db5..e7c49246fd88 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -634,9 +634,6 @@ struct net_device struct netdev_queue rx_queue; struct netdev_queue tx_queue ____cacheline_aligned_in_smp; - - struct Qdisc *qdisc_ingress; - unsigned long tx_queue_len; /* Max frames per queue allowed */ /* Partially transmitted GSO packet. */ diff --git a/net/core/dev.c b/net/core/dev.c index ce79c28d739d..ab760a954d99 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2033,7 +2033,7 @@ static int ing_filter(struct sk_buff *skb) rxq = &dev->rx_queue; spin_lock(&rxq->lock); - if ((q = dev->qdisc_ingress) != NULL) + if ((q = rxq->qdisc) != NULL) result = q->enqueue(skb, q); spin_unlock(&rxq->lock); @@ -2044,7 +2044,7 @@ static inline struct sk_buff *handle_ing(struct sk_buff *skb, struct packet_type **pt_prev, int *ret, struct net_device *orig_dev) { - if (!skb->dev->qdisc_ingress) + if (!skb->dev->rx_queue.qdisc) goto out; if (*pt_prev) { diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 2313fa7c97be..4003c280b69f 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -450,14 +450,15 @@ dev_graft_qdisc(struct net_device *dev, struct Qdisc *qdisc) qdisc_lock_tree(dev); if (qdisc && qdisc->flags&TCQ_F_INGRESS) { - oqdisc = dev->qdisc_ingress; + dev_queue = &dev->rx_queue; + oqdisc = dev_queue->qdisc; /* Prune old scheduler */ if (oqdisc && atomic_read(&oqdisc->refcnt) <= 1) { /* delete */ qdisc_reset(oqdisc); - dev->qdisc_ingress = NULL; + dev_queue->qdisc = NULL; } else { /* new */ - dev->qdisc_ingress = qdisc; + dev_queue->qdisc = qdisc; } } else { @@ -739,7 +740,7 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg) return -ENOENT; q = qdisc_leaf(p, clid); } else { /* ingress */ - q = dev->qdisc_ingress; + q = dev->rx_queue.qdisc; } } else { struct netdev_queue *dev_queue = &dev->tx_queue; @@ -814,7 +815,7 @@ replay: return -ENOENT; q = qdisc_leaf(p, clid); } else { /*ingress */ - q = dev->qdisc_ingress; + q = dev->rx_queue.qdisc; } } else { struct netdev_queue *dev_queue = &dev->tx_queue; -- cgit v1.2.3 From ee609cb36220d18c0cf476b066a5ab7e6f6d3a69 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 8 Jul 2008 22:58:37 -0700 Subject: netdev: Move next_sched into struct netdev_queue. We schedule queues, not the device, for output queue processing in BH. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 5 ++--- net/core/dev.c | 15 +++++++-------- 2 files changed, 9 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index e7c49246fd88..1379c822e51d 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -454,6 +454,7 @@ struct netdev_queue { struct Qdisc *qdisc; struct Qdisc *qdisc_sleeping; struct list_head qdisc_list; + struct netdev_queue *next_sched; }; /* @@ -545,8 +546,6 @@ struct net_device #define NETIF_F_V6_CSUM (NETIF_F_GEN_CSUM | NETIF_F_IPV6_CSUM) #define NETIF_F_ALL_CSUM (NETIF_F_V4_CSUM | NETIF_F_V6_CSUM) - struct net_device *next_sched; - /* Interface index. Unique device identifier */ int ifindex; int iflink; @@ -940,7 +939,7 @@ static inline int unregister_gifconf(unsigned int family) */ struct softnet_data { - struct net_device *output_queue; + struct netdev_queue *output_queue; struct sk_buff_head input_pkt_queue; struct list_head poll_list; struct sk_buff *completion_queue; diff --git a/net/core/dev.c b/net/core/dev.c index ab760a954d99..d6b8d3c3e6ec 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1323,13 +1323,14 @@ static void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev) void __netif_schedule(struct net_device *dev) { if (!test_and_set_bit(__LINK_STATE_SCHED, &dev->state)) { + struct netdev_queue *txq = &dev->tx_queue; unsigned long flags; struct softnet_data *sd; local_irq_save(flags); sd = &__get_cpu_var(softnet_data); - dev->next_sched = sd->output_queue; - sd->output_queue = dev; + txq->next_sched = sd->output_queue; + sd->output_queue = txq; raise_softirq_irqoff(NET_TX_SOFTIRQ); local_irq_restore(flags); } @@ -1912,7 +1913,7 @@ static void net_tx_action(struct softirq_action *h) } if (sd->output_queue) { - struct net_device *head; + struct netdev_queue *head; local_irq_disable(); head = sd->output_queue; @@ -1920,12 +1921,10 @@ static void net_tx_action(struct softirq_action *h) local_irq_enable(); while (head) { - struct net_device *dev = head; - struct netdev_queue *txq; + struct netdev_queue *txq = head; + struct net_device *dev = txq->dev; head = head->next_sched; - txq = &dev->tx_queue; - smp_mb__before_clear_bit(); clear_bit(__LINK_STATE_SCHED, &dev->state); @@ -4346,7 +4345,7 @@ static int dev_cpu_callback(struct notifier_block *nfb, void *ocpu) { struct sk_buff **list_skb; - struct net_device **list_net; + struct netdev_queue **list_net; struct sk_buff *skb; unsigned int cpu, oldcpu = (unsigned long)ocpu; struct softnet_data *sd, *oldsd; -- cgit v1.2.3 From 970565bbad0c7b98db0d14131a69e5a0f4445d49 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 8 Jul 2008 23:10:33 -0700 Subject: netdev: Move gso_skb into netdev_queue. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 4 +--- net/sched/sch_generic.c | 42 +++++++++++++++++++++++------------------- 2 files changed, 24 insertions(+), 22 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 1379c822e51d..aae6c6d153f2 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -452,6 +452,7 @@ struct netdev_queue { spinlock_t lock; struct net_device *dev; struct Qdisc *qdisc; + struct sk_buff *gso_skb; struct Qdisc *qdisc_sleeping; struct list_head qdisc_list; struct netdev_queue *next_sched; @@ -635,9 +636,6 @@ struct net_device struct netdev_queue tx_queue ____cacheline_aligned_in_smp; unsigned long tx_queue_len; /* Max frames per queue allowed */ - /* Partially transmitted GSO packet. */ - struct sk_buff *gso_skb; - /* * One part is mostly used on xmit path (device) */ diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index dda78ee314ec..8247a406a401 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -63,10 +63,11 @@ static inline int qdisc_qlen(struct Qdisc *q) } static inline int dev_requeue_skb(struct sk_buff *skb, struct net_device *dev, + struct netdev_queue *dev_queue, struct Qdisc *q) { if (unlikely(skb->next)) - dev->gso_skb = skb; + dev_queue->gso_skb = skb; else q->ops->requeue(skb, q); @@ -75,12 +76,13 @@ static inline int dev_requeue_skb(struct sk_buff *skb, struct net_device *dev, } static inline struct sk_buff *dev_dequeue_skb(struct net_device *dev, + struct netdev_queue *dev_queue, struct Qdisc *q) { struct sk_buff *skb; - if ((skb = dev->gso_skb)) - dev->gso_skb = NULL; + if ((skb = dev_queue->gso_skb)) + dev_queue->gso_skb = NULL; else skb = q->dequeue(q); @@ -89,6 +91,7 @@ static inline struct sk_buff *dev_dequeue_skb(struct net_device *dev, static inline int handle_dev_cpu_collision(struct sk_buff *skb, struct net_device *dev, + struct netdev_queue *dev_queue, struct Qdisc *q) { int ret; @@ -111,7 +114,7 @@ static inline int handle_dev_cpu_collision(struct sk_buff *skb, * some time. */ __get_cpu_var(netdev_rx_stat).cpu_collision++; - ret = dev_requeue_skb(skb, dev, q); + ret = dev_requeue_skb(skb, dev, dev_queue, q); } return ret; @@ -144,7 +147,7 @@ static inline int qdisc_restart(struct net_device *dev) int ret = NETDEV_TX_BUSY; /* Dequeue packet */ - if (unlikely((skb = dev_dequeue_skb(dev, q)) == NULL)) + if (unlikely((skb = dev_dequeue_skb(dev, txq, q)) == NULL)) return 0; @@ -167,7 +170,7 @@ static inline int qdisc_restart(struct net_device *dev) case NETDEV_TX_LOCKED: /* Driver try lock failed */ - ret = handle_dev_cpu_collision(skb, dev, q); + ret = handle_dev_cpu_collision(skb, dev, txq, q); break; default: @@ -176,7 +179,7 @@ static inline int qdisc_restart(struct net_device *dev) printk(KERN_WARNING "BUG %s code %d qlen %d\n", dev->name, ret, q->q.qlen); - ret = dev_requeue_skb(skb, dev, q); + ret = dev_requeue_skb(skb, dev, txq, q); break; } @@ -578,31 +581,32 @@ void dev_activate(struct net_device *dev) spin_unlock_bh(&txq->lock); } -static void dev_deactivate_queue(struct net_device *dev, - struct netdev_queue *dev_queue, +static void dev_deactivate_queue(struct netdev_queue *dev_queue, struct Qdisc *qdisc_default) { - struct Qdisc *qdisc = dev_queue->qdisc; + struct Qdisc *qdisc; + struct sk_buff *skb; + + spin_lock_bh(&dev_queue->lock); + qdisc = dev_queue->qdisc; if (qdisc) { dev_queue->qdisc = qdisc_default; qdisc_reset(qdisc); } + skb = dev_queue->gso_skb; + dev_queue->gso_skb = NULL; + + spin_unlock_bh(&dev_queue->lock); + + kfree_skb(skb); } void dev_deactivate(struct net_device *dev) { - struct sk_buff *skb; int running; - spin_lock_bh(&dev->tx_queue.lock); - dev_deactivate_queue(dev, &dev->tx_queue, &noop_qdisc); - - skb = dev->gso_skb; - dev->gso_skb = NULL; - spin_unlock_bh(&dev->tx_queue.lock); - - kfree_skb(skb); + dev_deactivate_queue(&dev->tx_queue, &noop_qdisc); dev_watchdog_down(dev); -- cgit v1.2.3 From 86d804e10a37cd86f16bf72386c37e843a98a74b Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 8 Jul 2008 23:11:25 -0700 Subject: netdev: Make netif_schedule() routines work with netdev_queue objects. Only plain netif_schedule() remains taking a net_device, mostly as a compatability item while we transition the rest of these interfaces. Everything else calls netif_schedule_queue() or __netif_schedule(), both of which take a netdev_queue pointer. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 17 ++++++++++++----- net/core/dev.c | 9 +++++---- net/mac80211/main.c | 4 ++-- net/sched/sch_api.c | 4 ++-- net/sched/sch_cbq.c | 2 +- net/sched/sch_generic.c | 10 +++++----- 6 files changed, 27 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index aae6c6d153f2..28aa8e77cee9 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -952,12 +952,19 @@ DECLARE_PER_CPU(struct softnet_data,softnet_data); #define HAVE_NETIF_QUEUE -extern void __netif_schedule(struct net_device *dev); +extern void __netif_schedule(struct netdev_queue *txq); -static inline void netif_schedule(struct net_device *dev) +static inline void netif_schedule_queue(struct netdev_queue *txq) { + struct net_device *dev = txq->dev; + if (!test_bit(__LINK_STATE_XOFF, &dev->state)) - __netif_schedule(dev); + __netif_schedule(txq); +} + +static inline void netif_schedule(struct net_device *dev) +{ + netif_schedule_queue(&dev->tx_queue); } /** @@ -987,7 +994,7 @@ static inline void netif_wake_queue(struct net_device *dev) } #endif if (test_and_clear_bit(__LINK_STATE_XOFF, &dev->state)) - __netif_schedule(dev); + __netif_schedule(&dev->tx_queue); } /** @@ -1103,7 +1110,7 @@ static inline void netif_wake_subqueue(struct net_device *dev, u16 queue_index) #endif if (test_and_clear_bit(__LINK_STATE_XOFF, &dev->egress_subqueue[queue_index].state)) - __netif_schedule(dev); + __netif_schedule(&dev->tx_queue); #endif } diff --git a/net/core/dev.c b/net/core/dev.c index d6b8d3c3e6ec..0dc888ad4217 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1320,12 +1320,13 @@ static void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev) } -void __netif_schedule(struct net_device *dev) +void __netif_schedule(struct netdev_queue *txq) { + struct net_device *dev = txq->dev; + if (!test_and_set_bit(__LINK_STATE_SCHED, &dev->state)) { - struct netdev_queue *txq = &dev->tx_queue; - unsigned long flags; struct softnet_data *sd; + unsigned long flags; local_irq_save(flags); sd = &__get_cpu_var(softnet_data); @@ -1932,7 +1933,7 @@ static void net_tx_action(struct softirq_action *h) qdisc_run(dev); spin_unlock(&txq->lock); } else { - netif_schedule(dev); + netif_schedule_queue(txq); } } } diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 2968baa66b91..1c4d3ba6b878 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -885,10 +885,10 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid) spin_unlock_bh(&txq->lock); /* we just requeued the all the frames that were in the removed - * queue, and since we might miss a softirq we do netif_schedule. + * queue, and since we might miss a softirq we do netif_schedule_queue. * ieee80211_wake_queue is not used here as this queue is not * necessarily stopped */ - netif_schedule(local->mdev); + netif_schedule_queue(txq); spin_lock_bh(&sta->lock); *state = HT_AGG_STATE_IDLE; sta->ampdu_mlme.addba_req_num[tid] = 0; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index e73bd68aa7ae..95873f8dd37c 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -282,11 +282,11 @@ static enum hrtimer_restart qdisc_watchdog(struct hrtimer *timer) { struct qdisc_watchdog *wd = container_of(timer, struct qdisc_watchdog, timer); - struct net_device *dev = qdisc_dev(wd->qdisc); + struct netdev_queue *txq = wd->qdisc->dev_queue; wd->qdisc->flags &= ~TCQ_F_THROTTLED; smp_wmb(); - netif_schedule(dev); + netif_schedule_queue(txq); return HRTIMER_NORESTART; } diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index 99ce3da2b0a4..4efc836cbf38 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -650,7 +650,7 @@ static enum hrtimer_restart cbq_undelay(struct hrtimer *timer) } sch->flags &= ~TCQ_F_THROTTLED; - netif_schedule(qdisc_dev(sch)); + netif_schedule_queue(sch->dev_queue); return HRTIMER_NORESTART; } diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 8247a406a401..407dfdb142a4 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -62,7 +62,7 @@ static inline int qdisc_qlen(struct Qdisc *q) return q->q.qlen; } -static inline int dev_requeue_skb(struct sk_buff *skb, struct net_device *dev, +static inline int dev_requeue_skb(struct sk_buff *skb, struct netdev_queue *dev_queue, struct Qdisc *q) { @@ -71,7 +71,7 @@ static inline int dev_requeue_skb(struct sk_buff *skb, struct net_device *dev, else q->ops->requeue(skb, q); - netif_schedule(dev); + netif_schedule_queue(dev_queue); return 0; } @@ -114,7 +114,7 @@ static inline int handle_dev_cpu_collision(struct sk_buff *skb, * some time. */ __get_cpu_var(netdev_rx_stat).cpu_collision++; - ret = dev_requeue_skb(skb, dev, dev_queue, q); + ret = dev_requeue_skb(skb, dev_queue, q); } return ret; @@ -179,7 +179,7 @@ static inline int qdisc_restart(struct net_device *dev) printk(KERN_WARNING "BUG %s code %d qlen %d\n", dev->name, ret, q->q.qlen); - ret = dev_requeue_skb(skb, dev, txq, q); + ret = dev_requeue_skb(skb, txq, q); break; } @@ -200,7 +200,7 @@ void __qdisc_run(struct net_device *dev) * 2. we've been doing it for too long. */ if (need_resched() || jiffies != start_time) { - netif_schedule(dev); + netif_schedule_queue(&dev->tx_queue); break; } } -- cgit v1.2.3 From c773e847ea8f6812804e40f52399c6921a00eab1 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 8 Jul 2008 23:13:53 -0700 Subject: netdev: Move _xmit_lock and xmit_lock_owner into netdev_queue. Accesses are mostly structured such that when there are multiple TX queues the code transformations will be a little bit simpler. Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 13 ++++++- drivers/net/hamradio/bpqether.c | 12 ++++++- drivers/net/macvlan.c | 14 +++++++- drivers/net/wireless/hostap/hostap_hw.c | 12 ++++++- include/linux/netdevice.h | 62 +++++++++++++++++++++------------ net/8021q/vlan_dev.c | 15 ++++++-- net/core/dev.c | 28 ++++++++++----- net/netrom/af_netrom.c | 12 ++++++- net/rose/af_rose.c | 12 ++++++- net/sched/sch_generic.c | 9 +++-- 10 files changed, 145 insertions(+), 44 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index d57b65dc2c72..dc733d75a5e9 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -5019,6 +5019,17 @@ static int bond_check_params(struct bond_params *params) static struct lock_class_key bonding_netdev_xmit_lock_key; +static void bond_set_lockdep_class_one(struct netdev_queue *txq) +{ + lockdep_set_class(&txq->_xmit_lock, + &bonding_netdev_xmit_lock_key); +} + +static void bond_set_lockdep_class(struct net_device *dev) +{ + bond_set_lockdep_class_one(&dev->tx_queue); +} + /* Create a new bond based on the specified name and bonding parameters. * If name is NULL, obtain a suitable "bond%d" name for us. * Caller must NOT hold rtnl_lock; we need to release it here before we @@ -5076,7 +5087,7 @@ int bond_create(char *name, struct bond_params *params) goto out_bond; } - lockdep_set_class(&bond_dev->_xmit_lock, &bonding_netdev_xmit_lock_key); + bond_set_lockdep_class(bond_dev); netif_carrier_off(bond_dev); diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c index 5f4b4c6c9f76..fb186b8c3d4d 100644 --- a/drivers/net/hamradio/bpqether.c +++ b/drivers/net/hamradio/bpqether.c @@ -124,6 +124,16 @@ static LIST_HEAD(bpq_devices); */ static struct lock_class_key bpq_netdev_xmit_lock_key; +static void bpq_set_lockdep_class_one(struct netdev_queue *txq) +{ + lockdep_set_class(&txq->_xmit_lock, &bpq_netdev_xmit_lock_key); +} + +static void bpq_set_lockdep_class(struct net_device *dev) +{ + bpq_set_lockdep_class_one(&dev->tx_queue); +} + /* ------------------------------------------------------------------------ */ @@ -523,7 +533,7 @@ static int bpq_new_device(struct net_device *edev) err = register_netdevice(ndev); if (err) goto error; - lockdep_set_class(&ndev->_xmit_lock, &bpq_netdev_xmit_lock_key); + bpq_set_lockdep_class(ndev); /* List protected by RTNL */ list_add_rcu(&bpq->bpq_list, &bpq_devices); diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index c36a03ae9bfb..c02ceaa4a216 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -277,6 +277,17 @@ static struct lock_class_key macvlan_netdev_xmit_lock_key; #define MACVLAN_STATE_MASK \ ((1<<__LINK_STATE_NOCARRIER) | (1<<__LINK_STATE_DORMANT)) +static void macvlan_set_lockdep_class_one(struct netdev_queue *txq) +{ + lockdep_set_class(&txq->_xmit_lock, + &macvlan_netdev_xmit_lock_key); +} + +static void macvlan_set_lockdep_class(struct net_device *dev) +{ + macvlan_set_lockdep_class_one(&dev->tx_queue); +} + static int macvlan_init(struct net_device *dev) { struct macvlan_dev *vlan = netdev_priv(dev); @@ -287,7 +298,8 @@ static int macvlan_init(struct net_device *dev) dev->features = lowerdev->features & MACVLAN_FEATURES; dev->iflink = lowerdev->ifindex; - lockdep_set_class(&dev->_xmit_lock, &macvlan_netdev_xmit_lock_key); + macvlan_set_lockdep_class(dev); + return 0; } diff --git a/drivers/net/wireless/hostap/hostap_hw.c b/drivers/net/wireless/hostap/hostap_hw.c index 09004a632ae7..c1f4bb005d92 100644 --- a/drivers/net/wireless/hostap/hostap_hw.c +++ b/drivers/net/wireless/hostap/hostap_hw.c @@ -3102,6 +3102,16 @@ static void prism2_clear_set_tim_queue(local_info_t *local) */ static struct lock_class_key hostap_netdev_xmit_lock_key; +static void prism2_set_lockdep_class_one(struct netdev_queue *txq) +{ + lockdep_set_class(&txq->_xmit_lock, + &hostap_netdev_xmit_lock_key); +} + +static void prism2_set_lockdep_class(struct net_device *dev) +{ + prism2_set_lockdep_class_one(&dev->tx_queue); +} static struct net_device * prism2_init_local_data(struct prism2_helper_functions *funcs, int card_idx, @@ -3268,7 +3278,7 @@ while (0) if (ret >= 0) ret = register_netdevice(dev); - lockdep_set_class(&dev->_xmit_lock, &hostap_netdev_xmit_lock_key); + prism2_set_lockdep_class(dev); rtnl_unlock(); if (ret < 0) { printk(KERN_WARNING "%s: register netdevice failed!\n", diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 28aa8e77cee9..c8d5f128858d 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -453,6 +453,8 @@ struct netdev_queue { struct net_device *dev; struct Qdisc *qdisc; struct sk_buff *gso_skb; + spinlock_t _xmit_lock; + int xmit_lock_owner; struct Qdisc *qdisc_sleeping; struct list_head qdisc_list; struct netdev_queue *next_sched; @@ -639,12 +641,6 @@ struct net_device /* * One part is mostly used on xmit path (device) */ - /* hard_start_xmit synchronizer */ - spinlock_t _xmit_lock ____cacheline_aligned_in_smp; - /* cpu id of processor entered to hard_start_xmit or -1, - if nobody entered there. - */ - int xmit_lock_owner; void *priv; /* pointer to private data */ int (*hard_start_xmit) (struct sk_buff *skb, struct net_device *dev); @@ -1402,52 +1398,72 @@ static inline void netif_rx_complete(struct net_device *dev, * * Get network device transmit lock */ -static inline void __netif_tx_lock(struct net_device *dev, int cpu) +static inline void __netif_tx_lock(struct netdev_queue *txq, int cpu) { - spin_lock(&dev->_xmit_lock); - dev->xmit_lock_owner = cpu; + spin_lock(&txq->_xmit_lock); + txq->xmit_lock_owner = cpu; } static inline void netif_tx_lock(struct net_device *dev) { - __netif_tx_lock(dev, smp_processor_id()); + __netif_tx_lock(&dev->tx_queue, smp_processor_id()); +} + +static inline void __netif_tx_lock_bh(struct netdev_queue *txq) +{ + spin_lock_bh(&txq->_xmit_lock); + txq->xmit_lock_owner = smp_processor_id(); } static inline void netif_tx_lock_bh(struct net_device *dev) { - spin_lock_bh(&dev->_xmit_lock); - dev->xmit_lock_owner = smp_processor_id(); + __netif_tx_lock_bh(&dev->tx_queue); } -static inline int netif_tx_trylock(struct net_device *dev) +static inline int __netif_tx_trylock(struct netdev_queue *txq) { - int ok = spin_trylock(&dev->_xmit_lock); + int ok = spin_trylock(&txq->_xmit_lock); if (likely(ok)) - dev->xmit_lock_owner = smp_processor_id(); + txq->xmit_lock_owner = smp_processor_id(); return ok; } +static inline int netif_tx_trylock(struct net_device *dev) +{ + return __netif_tx_trylock(&dev->tx_queue); +} + +static inline void __netif_tx_unlock(struct netdev_queue *txq) +{ + txq->xmit_lock_owner = -1; + spin_unlock(&txq->_xmit_lock); +} + static inline void netif_tx_unlock(struct net_device *dev) { - dev->xmit_lock_owner = -1; - spin_unlock(&dev->_xmit_lock); + __netif_tx_unlock(&dev->tx_queue); +} + +static inline void __netif_tx_unlock_bh(struct netdev_queue *txq) +{ + txq->xmit_lock_owner = -1; + spin_unlock_bh(&txq->_xmit_lock); } static inline void netif_tx_unlock_bh(struct net_device *dev) { - dev->xmit_lock_owner = -1; - spin_unlock_bh(&dev->_xmit_lock); + __netif_tx_unlock_bh(&dev->tx_queue); } -#define HARD_TX_LOCK(dev, cpu) { \ +#define HARD_TX_LOCK(dev, txq, cpu) { \ if ((dev->features & NETIF_F_LLTX) == 0) { \ - __netif_tx_lock(dev, cpu); \ + __netif_tx_lock(txq, cpu); \ } \ } -#define HARD_TX_UNLOCK(dev) { \ +#define HARD_TX_UNLOCK(dev, txq) { \ if ((dev->features & NETIF_F_LLTX) == 0) { \ - netif_tx_unlock(dev); \ + __netif_tx_unlock(txq); \ } \ } diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index b6e52c025fd8..8efa399823e3 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -627,6 +627,18 @@ static void vlan_dev_set_rx_mode(struct net_device *vlan_dev) */ static struct lock_class_key vlan_netdev_xmit_lock_key; +static void vlan_dev_set_lockdep_one(struct netdev_queue *txq, + int subclass) +{ + lockdep_set_class_and_subclass(&txq->_xmit_lock, + &vlan_netdev_xmit_lock_key, subclass); +} + +static void vlan_dev_set_lockdep_class(struct net_device *dev, int subclass) +{ + vlan_dev_set_lockdep_one(&dev->tx_queue, subclass); +} + static const struct header_ops vlan_header_ops = { .create = vlan_dev_hard_header, .rebuild = vlan_dev_rebuild_header, @@ -668,8 +680,7 @@ static int vlan_dev_init(struct net_device *dev) if (is_vlan_dev(real_dev)) subclass = 1; - lockdep_set_class_and_subclass(&dev->_xmit_lock, - &vlan_netdev_xmit_lock_key, subclass); + vlan_dev_set_lockdep_class(dev, subclass); return 0; } diff --git a/net/core/dev.c b/net/core/dev.c index 0218b0b9be80..a29a359b15d1 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -258,7 +258,7 @@ DEFINE_PER_CPU(struct softnet_data, softnet_data); #ifdef CONFIG_DEBUG_LOCK_ALLOC /* - * register_netdevice() inits dev->_xmit_lock and sets lockdep class + * register_netdevice() inits txq->_xmit_lock and sets lockdep class * according to dev->type */ static const unsigned short netdev_lock_type[] = @@ -1758,19 +1758,19 @@ gso: if (dev->flags & IFF_UP) { int cpu = smp_processor_id(); /* ok because BHs are off */ - if (dev->xmit_lock_owner != cpu) { + if (txq->xmit_lock_owner != cpu) { - HARD_TX_LOCK(dev, cpu); + HARD_TX_LOCK(dev, txq, cpu); if (!netif_queue_stopped(dev) && !netif_subqueue_stopped(dev, skb)) { rc = 0; if (!dev_hard_start_xmit(skb, dev)) { - HARD_TX_UNLOCK(dev); + HARD_TX_UNLOCK(dev, txq); goto out; } } - HARD_TX_UNLOCK(dev); + HARD_TX_UNLOCK(dev, txq); if (net_ratelimit()) printk(KERN_CRIT "Virtual device %s asks to " "queue packet!\n", dev->name); @@ -3761,6 +3761,20 @@ static void rollback_registered(struct net_device *dev) dev_put(dev); } +static void __netdev_init_queue_locks_one(struct netdev_queue *dev_queue, + struct net_device *dev) +{ + spin_lock_init(&dev_queue->_xmit_lock); + netdev_set_lockdep_class(&dev_queue->_xmit_lock, dev->type); + dev_queue->xmit_lock_owner = -1; +} + +static void netdev_init_queue_locks(struct net_device *dev) +{ + __netdev_init_queue_locks_one(&dev->tx_queue, dev); + __netdev_init_queue_locks_one(&dev->rx_queue, dev); +} + /** * register_netdevice - register a network device * @dev: device to register @@ -3795,9 +3809,7 @@ int register_netdevice(struct net_device *dev) BUG_ON(!dev_net(dev)); net = dev_net(dev); - spin_lock_init(&dev->_xmit_lock); - netdev_set_lockdep_class(&dev->_xmit_lock, dev->type); - dev->xmit_lock_owner = -1; + netdev_init_queue_locks(dev); dev->iflink = -1; diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index 74884f4a6255..819afc449e1e 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -74,6 +74,16 @@ static const struct proto_ops nr_proto_ops; */ static struct lock_class_key nr_netdev_xmit_lock_key; +static void nr_set_lockdep_one(struct netdev_queue *txq) +{ + lockdep_set_class(&txq->_xmit_lock, &nr_netdev_xmit_lock_key); +} + +static void nr_set_lockdep_key(struct net_device *dev) +{ + nr_set_lockdep_one(&dev->tx_queue); +} + /* * Socket removal during an interrupt is now safe. */ @@ -1430,7 +1440,7 @@ static int __init nr_proto_init(void) free_netdev(dev); goto fail; } - lockdep_set_class(&dev->_xmit_lock, &nr_netdev_xmit_lock_key); + nr_set_lockdep_key(dev); dev_nr[i] = dev; } diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index 46461a69cd0f..7dbbc0891623 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -75,6 +75,16 @@ ax25_address rose_callsign; */ static struct lock_class_key rose_netdev_xmit_lock_key; +static void rose_set_lockdep_one(struct netdev_queue *txq) +{ + lockdep_set_class(&txq->_xmit_lock, &rose_netdev_xmit_lock_key); +} + +static void rose_set_lockdep_key(struct net_device *dev) +{ + rose_set_lockdep_one(&dev->tx_queue); +} + /* * Convert a ROSE address into text. */ @@ -1576,7 +1586,7 @@ static int __init rose_proto_init(void) free_netdev(dev); goto fail; } - lockdep_set_class(&dev->_xmit_lock, &rose_netdev_xmit_lock_key); + rose_set_lockdep_key(dev); dev_rose[i] = dev; } diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index fcc7533f0bcc..b6a36d394663 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -92,10 +92,9 @@ static inline int handle_dev_cpu_collision(struct sk_buff *skb, struct netdev_queue *dev_queue, struct Qdisc *q) { - struct net_device *dev = dev_queue->dev; int ret; - if (unlikely(dev->xmit_lock_owner == smp_processor_id())) { + if (unlikely(dev_queue->xmit_lock_owner == smp_processor_id())) { /* * Same CPU holding the lock. It may be a transient * configuration error, when hard_start_xmit() recurses. We @@ -105,7 +104,7 @@ static inline int handle_dev_cpu_collision(struct sk_buff *skb, kfree_skb(skb); if (net_ratelimit()) printk(KERN_WARNING "Dead loop on netdevice %s, " - "fix it urgently!\n", dev->name); + "fix it urgently!\n", dev_queue->dev->name); ret = qdisc_qlen(q); } else { /* @@ -155,10 +154,10 @@ static inline int qdisc_restart(struct netdev_queue *txq) dev = txq->dev; - HARD_TX_LOCK(dev, smp_processor_id()); + HARD_TX_LOCK(dev, txq, smp_processor_id()); if (!netif_subqueue_stopped(dev, skb)) ret = dev_hard_start_xmit(skb, dev); - HARD_TX_UNLOCK(dev); + HARD_TX_UNLOCK(dev, txq); spin_lock(&txq->lock); q = txq->qdisc; -- cgit v1.2.3 From b19fa1fa91845234961c64dbd564671aa7c0fd27 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 8 Jul 2008 23:14:24 -0700 Subject: net: Delete NETDEVICES_MULTIQUEUE kconfig option. Multiple TX queue support is a core networking feature. Signed-off-by: David S. Miller --- Documentation/networking/multiqueue.txt | 79 +-------------------------------- drivers/net/Kconfig | 8 ---- drivers/net/cpmac.c | 14 ------ drivers/net/ixgbe/ixgbe_ethtool.c | 6 --- drivers/net/ixgbe/ixgbe_main.c | 40 ----------------- drivers/net/s2io.c | 47 ++++---------------- include/linux/netdevice.h | 14 ------ include/linux/skbuff.h | 10 ----- net/mac80211/Kconfig | 3 -- 9 files changed, 9 insertions(+), 212 deletions(-) (limited to 'include/linux') diff --git a/Documentation/networking/multiqueue.txt b/Documentation/networking/multiqueue.txt index ea5a42e8f79f..e6dc1ee9e8f1 100644 --- a/Documentation/networking/multiqueue.txt +++ b/Documentation/networking/multiqueue.txt @@ -3,19 +3,11 @@ =========================================== Section 1: Base driver requirements for implementing multiqueue support -Section 2: Qdisc support for multiqueue devices -Section 3: Brief howto using PRIO or RR for multiqueue devices - Intro: Kernel support for multiqueue devices --------------------------------------------------------- -Kernel support for multiqueue devices is only an API that is presented to the -netdevice layer for base drivers to implement. This feature is part of the -core networking stack, and all network devices will be running on the -multiqueue-aware stack. If a base driver only has one queue, then these -changes are transparent to that driver. - +Kernel support for multiqueue devices is always present. Section 1: Base driver requirements for implementing multiqueue support ----------------------------------------------------------------------- @@ -43,73 +35,4 @@ bitmap on device initialization. Below is an example from e1000: netdev->features |= NETIF_F_MULTI_QUEUE; #endif - -Section 2: Qdisc support for multiqueue devices ------------------------------------------------ - -Currently two qdiscs support multiqueue devices. A new round-robin qdisc, -sch_rr, and sch_prio. The qdisc is responsible for classifying the skb's to -bands and queues, and will store the queue mapping into skb->queue_mapping. -Use this field in the base driver to determine which queue to send the skb -to. - -sch_rr has been added for hardware that doesn't want scheduling policies from -software, so it's a straight round-robin qdisc. It uses the same syntax and -classification priomap that sch_prio uses, so it should be intuitive to -configure for people who've used sch_prio. - -In order to utilitize the multiqueue features of the qdiscs, the network -device layer needs to enable multiple queue support. This can be done by -selecting NETDEVICES_MULTIQUEUE under Drivers. - -The PRIO qdisc naturally plugs into a multiqueue device. If -NETDEVICES_MULTIQUEUE is selected, then on qdisc load, the number of -bands requested is compared to the number of queues on the hardware. If they -are equal, it sets a one-to-one mapping up between the queues and bands. If -they're not equal, it will not load the qdisc. This is the same behavior -for RR. Once the association is made, any skb that is classified will have -skb->queue_mapping set, which will allow the driver to properly queue skb's -to multiple queues. - - -Section 3: Brief howto using PRIO and RR for multiqueue devices ---------------------------------------------------------------- - -The userspace command 'tc,' part of the iproute2 package, is used to configure -qdiscs. To add the PRIO qdisc to your network device, assuming the device is -called eth0, run the following command: - -# tc qdisc add dev eth0 root handle 1: prio bands 4 multiqueue - -This will create 4 bands, 0 being highest priority, and associate those bands -to the queues on your NIC. Assuming eth0 has 4 Tx queues, the band mapping -would look like: - -band 0 => queue 0 -band 1 => queue 1 -band 2 => queue 2 -band 3 => queue 3 - -Traffic will begin flowing through each queue if your TOS values are assigning -traffic across the various bands. For example, ssh traffic will always try to -go out band 0 based on TOS -> Linux priority conversion (realtime traffic), -so it will be sent out queue 0. ICMP traffic (pings) fall into the "normal" -traffic classification, which is band 1. Therefore pings will be send out -queue 1 on the NIC. - -Note the use of the multiqueue keyword. This is only in versions of iproute2 -that support multiqueue networking devices; if this is omitted when loading -a qdisc onto a multiqueue device, the qdisc will load and operate the same -if it were loaded onto a single-queue device (i.e. - sends all traffic to -queue 0). - -Another alternative to multiqueue band allocation can be done by using the -multiqueue option and specify 0 bands. If this is the case, the qdisc will -allocate the number of bands to equal the number of queues that the device -reports, and bring the qdisc online. - -The behavior of tc filters remains the same, where it will override TOS priority -classification. - - Author: Peter P. Waskiewicz Jr. diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index ef733abc857d..4675c1bd6fb9 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -26,14 +26,6 @@ menuconfig NETDEVICES # that for each of the symbols. if NETDEVICES -config NETDEVICES_MULTIQUEUE - bool "Netdevice multiple hardware queue support" - ---help--- - Say Y here if you want to allow the network stack to use multiple - hardware TX queues on an ethernet device. - - Most people will say N here. - config IFB tristate "Intermediate Functional Block support" depends on NET_CLS_ACT diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c index 7f3f62e1b113..d630e2a72f42 100644 --- a/drivers/net/cpmac.c +++ b/drivers/net/cpmac.c @@ -569,11 +569,7 @@ static int cpmac_start_xmit(struct sk_buff *skb, struct net_device *dev) len = max(skb->len, ETH_ZLEN); queue = skb_get_queue_mapping(skb); -#ifdef CONFIG_NETDEVICES_MULTIQUEUE netif_stop_subqueue(dev, queue); -#else - netif_stop_queue(dev); -#endif desc = &priv->desc_ring[queue]; if (unlikely(desc->dataflags & CPMAC_OWN)) { @@ -626,24 +622,14 @@ static void cpmac_end_xmit(struct net_device *dev, int queue) dev_kfree_skb_irq(desc->skb); desc->skb = NULL; -#ifdef CONFIG_NETDEVICES_MULTIQUEUE if (netif_subqueue_stopped(dev, queue)) netif_wake_subqueue(dev, queue); -#else - if (netif_queue_stopped(dev)) - netif_wake_queue(dev); -#endif } else { if (netif_msg_tx_err(priv) && net_ratelimit()) printk(KERN_WARNING "%s: end_xmit: spurious interrupt\n", dev->name); -#ifdef CONFIG_NETDEVICES_MULTIQUEUE if (netif_subqueue_stopped(dev, queue)) netif_wake_subqueue(dev, queue); -#else - if (netif_queue_stopped(dev)) - netif_wake_queue(dev); -#endif } } diff --git a/drivers/net/ixgbe/ixgbe_ethtool.c b/drivers/net/ixgbe/ixgbe_ethtool.c index 12990b1fe7e4..81b769093d22 100644 --- a/drivers/net/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ixgbe/ixgbe_ethtool.c @@ -252,21 +252,15 @@ static int ixgbe_set_tso(struct net_device *netdev, u32 data) netdev->features |= NETIF_F_TSO; netdev->features |= NETIF_F_TSO6; } else { -#ifdef CONFIG_NETDEVICES_MULTIQUEUE struct ixgbe_adapter *adapter = netdev_priv(netdev); int i; -#endif netif_stop_queue(netdev); -#ifdef CONFIG_NETDEVICES_MULTIQUEUE for (i = 0; i < adapter->num_tx_queues; i++) netif_stop_subqueue(netdev, i); -#endif netdev->features &= ~NETIF_F_TSO; netdev->features &= ~NETIF_F_TSO6; -#ifdef CONFIG_NETDEVICES_MULTIQUEUE for (i = 0; i < adapter->num_tx_queues; i++) netif_start_subqueue(netdev, i); -#endif netif_start_queue(netdev); } return 0; diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index b37d618d8e2a..10a1c8c5cda1 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -266,28 +266,16 @@ static bool ixgbe_clean_tx_irq(struct ixgbe_adapter *adapter, * sees the new next_to_clean. */ smp_mb(); -#ifdef CONFIG_NETDEVICES_MULTIQUEUE if (__netif_subqueue_stopped(netdev, tx_ring->queue_index) && !test_bit(__IXGBE_DOWN, &adapter->state)) { netif_wake_subqueue(netdev, tx_ring->queue_index); adapter->restart_queue++; } -#else - if (netif_queue_stopped(netdev) && - !test_bit(__IXGBE_DOWN, &adapter->state)) { - netif_wake_queue(netdev); - adapter->restart_queue++; - } -#endif } if (adapter->detect_tx_hung) if (ixgbe_check_tx_hang(adapter, tx_ring, eop, eop_desc)) -#ifdef CONFIG_NETDEVICES_MULTIQUEUE netif_stop_subqueue(netdev, tx_ring->queue_index); -#else - netif_stop_queue(netdev); -#endif if (total_tx_packets >= tx_ring->work_limit) IXGBE_WRITE_REG(&adapter->hw, IXGBE_EICS, tx_ring->eims_value); @@ -2192,11 +2180,7 @@ static void __devinit ixgbe_set_num_queues(struct ixgbe_adapter *adapter) case (IXGBE_FLAG_RSS_ENABLED): rss_m = 0xF; nrq = rss_i; -#ifdef CONFIG_NETDEVICES_MULTIQUEUE ntq = rss_i; -#else - ntq = 1; -#endif break; case 0: default: @@ -2370,10 +2354,8 @@ try_msi: } out: -#ifdef CONFIG_NETDEVICES_MULTIQUEUE /* Notify the stack of the (possibly) reduced Tx Queue count. */ adapter->netdev->egress_subqueue_count = adapter->num_tx_queues; -#endif return err; } @@ -2910,9 +2892,7 @@ static void ixgbe_watchdog(unsigned long data) struct net_device *netdev = adapter->netdev; bool link_up; u32 link_speed = 0; -#ifdef CONFIG_NETDEVICES_MULTIQUEUE int i; -#endif adapter->hw.mac.ops.check_link(&adapter->hw, &(link_speed), &link_up); @@ -2934,10 +2914,8 @@ static void ixgbe_watchdog(unsigned long data) netif_carrier_on(netdev); netif_wake_queue(netdev); -#ifdef CONFIG_NETDEVICES_MULTIQUEUE for (i = 0; i < adapter->num_tx_queues; i++) netif_wake_subqueue(netdev, i); -#endif } else { /* Force detection of hung controller */ adapter->detect_tx_hung = true; @@ -3264,11 +3242,7 @@ static int __ixgbe_maybe_stop_tx(struct net_device *netdev, { struct ixgbe_adapter *adapter = netdev_priv(netdev); -#ifdef CONFIG_NETDEVICES_MULTIQUEUE netif_stop_subqueue(netdev, tx_ring->queue_index); -#else - netif_stop_queue(netdev); -#endif /* Herbert's original patch had: * smp_mb__after_netif_stop_queue(); * but since that doesn't exist yet, just open code it. */ @@ -3280,11 +3254,7 @@ static int __ixgbe_maybe_stop_tx(struct net_device *netdev, return -EBUSY; /* A reprieve! - use start_queue because it doesn't call schedule */ -#ifdef CONFIG_NETDEVICES_MULTIQUEUE netif_wake_subqueue(netdev, tx_ring->queue_index); -#else - netif_wake_queue(netdev); -#endif ++adapter->restart_queue; return 0; } @@ -3312,9 +3282,7 @@ static int ixgbe_xmit_frame(struct sk_buff *skb, struct net_device *netdev) unsigned int f; unsigned int nr_frags = skb_shinfo(skb)->nr_frags; len -= skb->data_len; -#ifdef CONFIG_NETDEVICES_MULTIQUEUE r_idx = (adapter->num_tx_queues - 1) & skb->queue_mapping; -#endif tx_ring = &adapter->tx_ring[r_idx]; @@ -3502,11 +3470,7 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, pci_set_master(pdev); pci_save_state(pdev); -#ifdef CONFIG_NETDEVICES_MULTIQUEUE netdev = alloc_etherdev_mq(sizeof(struct ixgbe_adapter), MAX_TX_QUEUES); -#else - netdev = alloc_etherdev(sizeof(struct ixgbe_adapter)); -#endif if (!netdev) { err = -ENOMEM; goto err_alloc_etherdev; @@ -3598,9 +3562,7 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, if (pci_using_dac) netdev->features |= NETIF_F_HIGHDMA; -#ifdef CONFIG_NETDEVICES_MULTIQUEUE netdev->features |= NETIF_F_MULTI_QUEUE; -#endif /* make sure the EEPROM is good */ if (ixgbe_validate_eeprom_checksum(hw, NULL) < 0) { @@ -3668,10 +3630,8 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, netif_carrier_off(netdev); netif_stop_queue(netdev); -#ifdef CONFIG_NETDEVICES_MULTIQUEUE for (i = 0; i < adapter->num_tx_queues; i++) netif_stop_subqueue(netdev, i); -#endif ixgbe_napi_add_all(adapter); diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c index e7a3dbec674c..51a91154125d 100644 --- a/drivers/net/s2io.c +++ b/drivers/net/s2io.c @@ -546,13 +546,10 @@ static struct pci_driver s2io_driver = { static inline void s2io_stop_all_tx_queue(struct s2io_nic *sp) { int i; -#ifdef CONFIG_NETDEVICES_MULTIQUEUE if (sp->config.multiq) { for (i = 0; i < sp->config.tx_fifo_num; i++) netif_stop_subqueue(sp->dev, i); - } else -#endif - { + } else { for (i = 0; i < sp->config.tx_fifo_num; i++) sp->mac_control.fifos[i].queue_state = FIFO_QUEUE_STOP; netif_stop_queue(sp->dev); @@ -561,12 +558,9 @@ static inline void s2io_stop_all_tx_queue(struct s2io_nic *sp) static inline void s2io_stop_tx_queue(struct s2io_nic *sp, int fifo_no) { -#ifdef CONFIG_NETDEVICES_MULTIQUEUE if (sp->config.multiq) netif_stop_subqueue(sp->dev, fifo_no); - else -#endif - { + else { sp->mac_control.fifos[fifo_no].queue_state = FIFO_QUEUE_STOP; netif_stop_queue(sp->dev); @@ -576,13 +570,10 @@ static inline void s2io_stop_tx_queue(struct s2io_nic *sp, int fifo_no) static inline void s2io_start_all_tx_queue(struct s2io_nic *sp) { int i; -#ifdef CONFIG_NETDEVICES_MULTIQUEUE if (sp->config.multiq) { for (i = 0; i < sp->config.tx_fifo_num; i++) netif_start_subqueue(sp->dev, i); - } else -#endif - { + } else { for (i = 0; i < sp->config.tx_fifo_num; i++) sp->mac_control.fifos[i].queue_state = FIFO_QUEUE_START; netif_start_queue(sp->dev); @@ -591,12 +582,9 @@ static inline void s2io_start_all_tx_queue(struct s2io_nic *sp) static inline void s2io_start_tx_queue(struct s2io_nic *sp, int fifo_no) { -#ifdef CONFIG_NETDEVICES_MULTIQUEUE if (sp->config.multiq) netif_start_subqueue(sp->dev, fifo_no); - else -#endif - { + else { sp->mac_control.fifos[fifo_no].queue_state = FIFO_QUEUE_START; netif_start_queue(sp->dev); @@ -606,13 +594,10 @@ static inline void s2io_start_tx_queue(struct s2io_nic *sp, int fifo_no) static inline void s2io_wake_all_tx_queue(struct s2io_nic *sp) { int i; -#ifdef CONFIG_NETDEVICES_MULTIQUEUE if (sp->config.multiq) { for (i = 0; i < sp->config.tx_fifo_num; i++) netif_wake_subqueue(sp->dev, i); - } else -#endif - { + } else { for (i = 0; i < sp->config.tx_fifo_num; i++) sp->mac_control.fifos[i].queue_state = FIFO_QUEUE_START; netif_wake_queue(sp->dev); @@ -623,13 +608,10 @@ static inline void s2io_wake_tx_queue( struct fifo_info *fifo, int cnt, u8 multiq) { -#ifdef CONFIG_NETDEVICES_MULTIQUEUE if (multiq) { if (cnt && __netif_subqueue_stopped(fifo->dev, fifo->fifo_no)) netif_wake_subqueue(fifo->dev, fifo->fifo_no); - } else -#endif - if (cnt && (fifo->queue_state == FIFO_QUEUE_STOP)) { + } else if (cnt && (fifo->queue_state == FIFO_QUEUE_STOP)) { if (netif_queue_stopped(fifo->dev)) { fifo->queue_state = FIFO_QUEUE_START; netif_wake_queue(fifo->dev); @@ -4189,15 +4171,12 @@ static int s2io_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_LOCKED; } -#ifdef CONFIG_NETDEVICES_MULTIQUEUE if (sp->config.multiq) { if (__netif_subqueue_stopped(dev, fifo->fifo_no)) { spin_unlock_irqrestore(&fifo->tx_lock, flags); return NETDEV_TX_BUSY; } - } else -#endif - if (unlikely(fifo->queue_state == FIFO_QUEUE_STOP)) { + } else if (unlikely(fifo->queue_state == FIFO_QUEUE_STOP)) { if (netif_queue_stopped(dev)) { spin_unlock_irqrestore(&fifo->tx_lock, flags); return NETDEV_TX_BUSY; @@ -7633,12 +7612,6 @@ static int s2io_verify_parm(struct pci_dev *pdev, u8 *dev_intr_type, DBG_PRINT(ERR_DBG, "tx fifos\n"); } -#ifndef CONFIG_NETDEVICES_MULTIQUEUE - if (multiq) { - DBG_PRINT(ERR_DBG, "s2io: Multiqueue support not enabled\n"); - multiq = 0; - } -#endif if (multiq) *dev_multiq = multiq; @@ -7783,12 +7756,10 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre) pci_disable_device(pdev); return -ENODEV; } -#ifdef CONFIG_NETDEVICES_MULTIQUEUE if (dev_multiq) dev = alloc_etherdev_mq(sizeof(struct s2io_nic), tx_fifo_num); else -#endif - dev = alloc_etherdev(sizeof(struct s2io_nic)); + dev = alloc_etherdev(sizeof(struct s2io_nic)); if (dev == NULL) { DBG_PRINT(ERR_DBG, "Device allocation failed\n"); pci_disable_device(pdev); @@ -7979,10 +7950,8 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre) dev->features |= NETIF_F_UFO; dev->features |= NETIF_F_HW_CSUM; } -#ifdef CONFIG_NETDEVICES_MULTIQUEUE if (config->multiq) dev->features |= NETIF_F_MULTI_QUEUE; -#endif dev->tx_timeout = &s2io_tx_watchdog; dev->watchdog_timeo = WATCH_DOG_TIMEOUT; INIT_WORK(&sp->rst_timer_task, s2io_restart_nic); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index c8d5f128858d..e2d931f9b700 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1043,9 +1043,7 @@ static inline int netif_running(const struct net_device *dev) */ static inline void netif_start_subqueue(struct net_device *dev, u16 queue_index) { -#ifdef CONFIG_NETDEVICES_MULTIQUEUE clear_bit(__LINK_STATE_XOFF, &dev->egress_subqueue[queue_index].state); -#endif } /** @@ -1057,13 +1055,11 @@ static inline void netif_start_subqueue(struct net_device *dev, u16 queue_index) */ static inline void netif_stop_subqueue(struct net_device *dev, u16 queue_index) { -#ifdef CONFIG_NETDEVICES_MULTIQUEUE #ifdef CONFIG_NETPOLL_TRAP if (netpoll_trap()) return; #endif set_bit(__LINK_STATE_XOFF, &dev->egress_subqueue[queue_index].state); -#endif } /** @@ -1076,12 +1072,8 @@ static inline void netif_stop_subqueue(struct net_device *dev, u16 queue_index) static inline int __netif_subqueue_stopped(const struct net_device *dev, u16 queue_index) { -#ifdef CONFIG_NETDEVICES_MULTIQUEUE return test_bit(__LINK_STATE_XOFF, &dev->egress_subqueue[queue_index].state); -#else - return 0; -#endif } static inline int netif_subqueue_stopped(const struct net_device *dev, @@ -1099,7 +1091,6 @@ static inline int netif_subqueue_stopped(const struct net_device *dev, */ static inline void netif_wake_subqueue(struct net_device *dev, u16 queue_index) { -#ifdef CONFIG_NETDEVICES_MULTIQUEUE #ifdef CONFIG_NETPOLL_TRAP if (netpoll_trap()) return; @@ -1107,7 +1098,6 @@ static inline void netif_wake_subqueue(struct net_device *dev, u16 queue_index) if (test_and_clear_bit(__LINK_STATE_XOFF, &dev->egress_subqueue[queue_index].state)) __netif_schedule(&dev->tx_queue); -#endif } /** @@ -1119,11 +1109,7 @@ static inline void netif_wake_subqueue(struct net_device *dev, u16 queue_index) */ static inline int netif_is_multiqueue(const struct net_device *dev) { -#ifdef CONFIG_NETDEVICES_MULTIQUEUE return (!!(NETIF_F_MULTI_QUEUE & dev->features)); -#else - return 0; -#endif } /* Use this variant when it is known for sure that it diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 2220b9e2dab0..8f10e3d08fd9 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -305,9 +305,7 @@ struct sk_buff { #endif int iif; -#ifdef CONFIG_NETDEVICES_MULTIQUEUE __u16 queue_mapping; -#endif #ifdef CONFIG_NET_SCHED __u16 tc_index; /* traffic control index */ #ifdef CONFIG_NET_CLS_ACT @@ -1671,25 +1669,17 @@ static inline void skb_init_secmark(struct sk_buff *skb) static inline void skb_set_queue_mapping(struct sk_buff *skb, u16 queue_mapping) { -#ifdef CONFIG_NETDEVICES_MULTIQUEUE skb->queue_mapping = queue_mapping; -#endif } static inline u16 skb_get_queue_mapping(struct sk_buff *skb) { -#ifdef CONFIG_NETDEVICES_MULTIQUEUE return skb->queue_mapping; -#else - return 0; -#endif } static inline void skb_copy_queue_mapping(struct sk_buff *to, const struct sk_buff *from) { -#ifdef CONFIG_NETDEVICES_MULTIQUEUE to->queue_mapping = from->queue_mapping; -#endif } static inline int skb_is_gso(const struct sk_buff *skb) diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index 40f1add17753..d2038418e2bd 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -15,14 +15,11 @@ config MAC80211_QOS def_bool y depends on MAC80211 depends on NET_SCHED - depends on NETDEVICES_MULTIQUEUE comment "QoS/HT support disabled" depends on MAC80211 && !MAC80211_QOS comment "QoS/HT support needs CONFIG_NET_SCHED" depends on MAC80211 && !NET_SCHED -comment "QoS/HT support needs CONFIG_NETDEVICES_MULTIQUEUE" - depends on MAC80211 && !NETDEVICES_MULTIQUEUE menu "Rate control algorithm selection" depends on MAC80211 != n -- cgit v1.2.3 From 79d16385c7f287a33ea771c4dbe60ae43f791b49 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 8 Jul 2008 23:14:46 -0700 Subject: netdev: Move atomic queue state bits into netdev_queue. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 55 +++++++++++++++++++++++++++++++++-------------- include/net/pkt_sched.h | 2 +- net/sched/sch_generic.c | 20 +++++++++-------- 3 files changed, 51 insertions(+), 26 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index e2d931f9b700..203c5504fe43 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -281,14 +281,12 @@ struct header_ops { enum netdev_state_t { - __LINK_STATE_XOFF=0, __LINK_STATE_START, __LINK_STATE_PRESENT, __LINK_STATE_SCHED, __LINK_STATE_NOCARRIER, __LINK_STATE_LINKWATCH_PENDING, __LINK_STATE_DORMANT, - __LINK_STATE_QDISC_RUNNING, }; @@ -448,10 +446,17 @@ static inline void napi_synchronize(const struct napi_struct *n) # define napi_synchronize(n) barrier() #endif +enum netdev_queue_state_t +{ + __QUEUE_STATE_XOFF, + __QUEUE_STATE_QDISC_RUNNING, +}; + struct netdev_queue { spinlock_t lock; struct net_device *dev; struct Qdisc *qdisc; + unsigned long state; struct sk_buff *gso_skb; spinlock_t _xmit_lock; int xmit_lock_owner; @@ -952,9 +957,7 @@ extern void __netif_schedule(struct netdev_queue *txq); static inline void netif_schedule_queue(struct netdev_queue *txq) { - struct net_device *dev = txq->dev; - - if (!test_bit(__LINK_STATE_XOFF, &dev->state)) + if (!test_bit(__QUEUE_STATE_XOFF, &txq->state)) __netif_schedule(txq); } @@ -969,9 +972,14 @@ static inline void netif_schedule(struct net_device *dev) * * Allow upper layers to call the device hard_start_xmit routine. */ +static inline void netif_tx_start_queue(struct netdev_queue *dev_queue) +{ + clear_bit(__QUEUE_STATE_XOFF, &dev_queue->state); +} + static inline void netif_start_queue(struct net_device *dev) { - clear_bit(__LINK_STATE_XOFF, &dev->state); + netif_tx_start_queue(&dev->tx_queue); } /** @@ -981,16 +989,21 @@ static inline void netif_start_queue(struct net_device *dev) * Allow upper layers to call the device hard_start_xmit routine. * Used for flow control when transmit resources are available. */ -static inline void netif_wake_queue(struct net_device *dev) +static inline void netif_tx_wake_queue(struct netdev_queue *dev_queue) { #ifdef CONFIG_NETPOLL_TRAP if (netpoll_trap()) { - clear_bit(__LINK_STATE_XOFF, &dev->state); + clear_bit(__QUEUE_STATE_XOFF, &dev_queue->state); return; } #endif - if (test_and_clear_bit(__LINK_STATE_XOFF, &dev->state)) - __netif_schedule(&dev->tx_queue); + if (test_and_clear_bit(__QUEUE_STATE_XOFF, &dev_queue->state)) + __netif_schedule(dev_queue); +} + +static inline void netif_wake_queue(struct net_device *dev) +{ + netif_tx_wake_queue(&dev->tx_queue); } /** @@ -1000,9 +1013,14 @@ static inline void netif_wake_queue(struct net_device *dev) * Stop upper layers calling the device hard_start_xmit routine. * Used for flow control when transmit resources are unavailable. */ +static inline void netif_tx_stop_queue(struct netdev_queue *dev_queue) +{ + set_bit(__QUEUE_STATE_XOFF, &dev_queue->state); +} + static inline void netif_stop_queue(struct net_device *dev) { - set_bit(__LINK_STATE_XOFF, &dev->state); + netif_tx_stop_queue(&dev->tx_queue); } /** @@ -1011,9 +1029,14 @@ static inline void netif_stop_queue(struct net_device *dev) * * Test if transmit queue on device is currently unable to send. */ +static inline int netif_tx_queue_stopped(const struct netdev_queue *dev_queue) +{ + return test_bit(__QUEUE_STATE_XOFF, &dev_queue->state); +} + static inline int netif_queue_stopped(const struct net_device *dev) { - return test_bit(__LINK_STATE_XOFF, &dev->state); + return netif_tx_queue_stopped(&dev->tx_queue); } /** @@ -1043,7 +1066,7 @@ static inline int netif_running(const struct net_device *dev) */ static inline void netif_start_subqueue(struct net_device *dev, u16 queue_index) { - clear_bit(__LINK_STATE_XOFF, &dev->egress_subqueue[queue_index].state); + clear_bit(__QUEUE_STATE_XOFF, &dev->egress_subqueue[queue_index].state); } /** @@ -1059,7 +1082,7 @@ static inline void netif_stop_subqueue(struct net_device *dev, u16 queue_index) if (netpoll_trap()) return; #endif - set_bit(__LINK_STATE_XOFF, &dev->egress_subqueue[queue_index].state); + set_bit(__QUEUE_STATE_XOFF, &dev->egress_subqueue[queue_index].state); } /** @@ -1072,7 +1095,7 @@ static inline void netif_stop_subqueue(struct net_device *dev, u16 queue_index) static inline int __netif_subqueue_stopped(const struct net_device *dev, u16 queue_index) { - return test_bit(__LINK_STATE_XOFF, + return test_bit(__QUEUE_STATE_XOFF, &dev->egress_subqueue[queue_index].state); } @@ -1095,7 +1118,7 @@ static inline void netif_wake_subqueue(struct net_device *dev, u16 queue_index) if (netpoll_trap()) return; #endif - if (test_and_clear_bit(__LINK_STATE_XOFF, + if (test_and_clear_bit(__QUEUE_STATE_XOFF, &dev->egress_subqueue[queue_index].state)) __netif_schedule(&dev->tx_queue); } diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h index 2311d242bb35..d58c1a5eb845 100644 --- a/include/net/pkt_sched.h +++ b/include/net/pkt_sched.h @@ -91,7 +91,7 @@ static inline void qdisc_run(struct netdev_queue *txq) struct net_device *dev = txq->dev; if (!netif_queue_stopped(dev) && - !test_and_set_bit(__LINK_STATE_QDISC_RUNNING, &dev->state)) + !test_and_set_bit(__QUEUE_STATE_QDISC_RUNNING, &txq->state)) __qdisc_run(txq); } diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index b6a36d394663..243de935b182 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -121,9 +121,9 @@ static inline int handle_dev_cpu_collision(struct sk_buff *skb, /* * NOTE: Called under queue->lock with locally disabled BH. * - * __LINK_STATE_QDISC_RUNNING guarantees only one CPU can process this - * device at a time. queue->lock serializes queue accesses for - * this device AND txq->qdisc pointer itself. + * __QUEUE_STATE_QDISC_RUNNING guarantees only one CPU can process + * this queue at a time. queue->lock serializes queue accesses for + * this queue AND txq->qdisc pointer itself. * * netif_tx_lock serializes accesses to device driver. * @@ -206,7 +206,7 @@ void __qdisc_run(struct netdev_queue *txq) } } - clear_bit(__LINK_STATE_QDISC_RUNNING, &dev->state); + clear_bit(__QUEUE_STATE_QDISC_RUNNING, &txq->state); } static void dev_watchdog(unsigned long arg) @@ -605,9 +605,10 @@ static void dev_deactivate_queue(struct netdev_queue *dev_queue, void dev_deactivate(struct net_device *dev) { + struct netdev_queue *dev_queue = &dev->tx_queue; int running; - dev_deactivate_queue(&dev->tx_queue, &noop_qdisc); + dev_deactivate_queue(dev_queue, &noop_qdisc); dev_watchdog_down(dev); @@ -616,16 +617,17 @@ void dev_deactivate(struct net_device *dev) /* Wait for outstanding qdisc_run calls. */ do { - while (test_bit(__LINK_STATE_QDISC_RUNNING, &dev->state)) + while (test_bit(__QUEUE_STATE_QDISC_RUNNING, &dev_queue->state)) yield(); /* * Double-check inside queue lock to ensure that all effects * of the queue run are visible when we return. */ - spin_lock_bh(&dev->tx_queue.lock); - running = test_bit(__LINK_STATE_QDISC_RUNNING, &dev->state); - spin_unlock_bh(&dev->tx_queue.lock); + spin_lock_bh(&dev_queue->lock); + running = test_bit(__QUEUE_STATE_QDISC_RUNNING, + &dev_queue->state); + spin_unlock_bh(&dev_queue->lock); /* * The running flag should never be set at this point because -- cgit v1.2.3 From d8156534040996f6a93a24d3592d5d587f2587e5 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Tue, 8 Jul 2008 15:13:05 -0700 Subject: net: add netif_napi_del function to allow for removal of napistructs Adds netif_napi_del function which is used to remove the napi struct from the netdev napi_list in cases where CONFIG_NETPOLL was enabled. The motivation for adding this is to handle the case in which the number of queues on a device changes due to a configuration change. Previously the napi structs for each queue would be left in the list until the netdev was freed. Signed-off-by: Alexander Duyck Signed-off-by: Jeff Kirsher Signed-off-by: Jeff Garzik --- include/linux/netdevice.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 203c5504fe43..b54ec16dfbda 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -828,6 +828,19 @@ static inline void netif_napi_add(struct net_device *dev, set_bit(NAPI_STATE_SCHED, &napi->state); } +/** + * netif_napi_del - remove a napi context + * @napi: napi context + * + * netif_napi_del() removes a napi context from the network device napi list + */ +static inline void netif_napi_del(struct napi_struct *napi) +{ +#ifdef CONFIG_NETPOLL + list_del(&napi->dev_list); +#endif +} + struct packet_type { __be16 type; /* This is really htons(ether_type). */ struct net_device *dev; /* NULL is wildcarded here */ -- cgit v1.2.3 From 0f420358e3a2abc028320ace7783e2e38cae77bf Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Fri, 11 Jul 2008 22:02:23 +1000 Subject: md: Turn rdev->sb_offset into a sector-based quantity. Rename it to sb_start to make sure all users have been converted. Signed-off-by: Andre Noll Signed-off-by: Neil Brown --- drivers/md/bitmap.c | 10 +++--- drivers/md/md.c | 81 ++++++++++++++++++++++------------------------- include/linux/raid/md_k.h | 2 +- 3 files changed, 44 insertions(+), 49 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c index dedba16d42f7..eba83e25b678 100644 --- a/drivers/md/bitmap.c +++ b/drivers/md/bitmap.c @@ -225,7 +225,7 @@ static struct page *read_sb_page(mddev_t *mddev, long offset, unsigned long inde || test_bit(Faulty, &rdev->flags)) continue; - target = (rdev->sb_offset << 1) + offset + index * (PAGE_SIZE/512); + target = rdev->sb_start + offset + index * (PAGE_SIZE/512); if (sync_page_io(rdev->bdev, target, PAGE_SIZE, page, READ)) { page->index = index; @@ -262,12 +262,12 @@ static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait) /* bitmap runs in to metadata */ return -EINVAL; if (rdev->data_offset + mddev->size*2 - > rdev->sb_offset*2 + bitmap->offset) + > rdev->sb_start + bitmap->offset) /* data runs in to bitmap */ return -EINVAL; - } else if (rdev->sb_offset*2 < rdev->data_offset) { + } else if (rdev->sb_start < rdev->data_offset) { /* METADATA BITMAP DATA */ - if (rdev->sb_offset*2 + if (rdev->sb_start + bitmap->offset + page->index*(PAGE_SIZE/512) + size/512 > rdev->data_offset) @@ -277,7 +277,7 @@ static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait) /* DATA METADATA BITMAP - no problems */ } md_super_write(mddev, rdev, - (rdev->sb_offset<<1) + bitmap->offset + rdev->sb_start + bitmap->offset + page->index * (PAGE_SIZE/512), size, page); diff --git a/drivers/md/md.c b/drivers/md/md.c index 3276edde7576..5590cb54b584 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -356,7 +356,7 @@ static inline sector_t calc_dev_sboffset(struct block_device *bdev) static sector_t calc_num_sectors(mdk_rdev_t *rdev, unsigned chunk_size) { - sector_t num_sectors = rdev->sb_offset * 2; + sector_t num_sectors = rdev->sb_start; if (chunk_size) num_sectors &= ~((sector_t)chunk_size/512 - 1); @@ -383,7 +383,7 @@ static void free_disk_sb(mdk_rdev_t * rdev) put_page(rdev->sb_page); rdev->sb_loaded = 0; rdev->sb_page = NULL; - rdev->sb_offset = 0; + rdev->sb_start = 0; rdev->size = 0; } } @@ -529,7 +529,7 @@ static int read_disk_sb(mdk_rdev_t * rdev, int size) return 0; - if (!sync_page_io(rdev->bdev, rdev->sb_offset<<1, size, rdev->sb_page, READ)) + if (!sync_page_io(rdev->bdev, rdev->sb_start, size, rdev->sb_page, READ)) goto fail; rdev->sb_loaded = 1; return 0; @@ -666,16 +666,14 @@ static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE]; mdp_super_t *sb; int ret; - sector_t sb_offset; /* - * Calculate the position of the superblock, + * Calculate the position of the superblock (512byte sectors), * it's at the end of the disk. * * It also happens to be a multiple of 4Kb. */ - sb_offset = calc_dev_sboffset(rdev->bdev) / 2; - rdev->sb_offset = sb_offset; + rdev->sb_start = calc_dev_sboffset(rdev->bdev); ret = read_disk_sb(rdev, MD_SB_BYTES); if (ret) return ret; @@ -1007,10 +1005,10 @@ super_90_rdev_size_change(mdk_rdev_t *rdev, unsigned long long size) size *= 2; /* convert to sectors */ if (rdev->mddev->bitmap_offset) return 0; /* can't move bitmap */ - rdev->sb_offset = calc_dev_sboffset(rdev->bdev) / 2; - if (!size || size > rdev->sb_offset*2) - size = rdev->sb_offset*2; - md_super_write(rdev->mddev, rdev, rdev->sb_offset << 1, rdev->sb_size, + rdev->sb_start = calc_dev_sboffset(rdev->bdev); + if (!size || size > rdev->sb_start) + size = rdev->sb_start; + md_super_write(rdev->mddev, rdev, rdev->sb_start, rdev->sb_size, rdev->sb_page); md_super_wait(rdev->mddev); return size/2; /* kB for sysfs */ @@ -1048,12 +1046,12 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) { struct mdp_superblock_1 *sb; int ret; - sector_t sb_offset; + sector_t sb_start; char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE]; int bmask; /* - * Calculate the position of the superblock. + * Calculate the position of the superblock in 512byte sectors. * It is always aligned to a 4K boundary and * depeding on minor_version, it can be: * 0: At least 8K, but less than 12K, from end of device @@ -1062,22 +1060,20 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) */ switch(minor_version) { case 0: - sb_offset = rdev->bdev->bd_inode->i_size >> 9; - sb_offset -= 8*2; - sb_offset &= ~(sector_t)(4*2-1); - /* convert from sectors to K */ - sb_offset /= 2; + sb_start = rdev->bdev->bd_inode->i_size >> 9; + sb_start -= 8*2; + sb_start &= ~(sector_t)(4*2-1); break; case 1: - sb_offset = 0; + sb_start = 0; break; case 2: - sb_offset = 4; + sb_start = 8; break; default: return -EINVAL; } - rdev->sb_offset = sb_offset; + rdev->sb_start = sb_start; /* superblock is rarely larger than 1K, but it can be larger, * and it is safe to read 4k, so we do that @@ -1091,7 +1087,7 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) if (sb->magic != cpu_to_le32(MD_SB_MAGIC) || sb->major_version != cpu_to_le32(1) || le32_to_cpu(sb->max_dev) > (4096-256)/2 || - le64_to_cpu(sb->super_offset) != (rdev->sb_offset<<1) || + le64_to_cpu(sb->super_offset) != rdev->sb_start || (le32_to_cpu(sb->feature_map) & ~MD_FEATURE_ALL) != 0) return -EINVAL; @@ -1127,7 +1123,7 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) rdev->sb_size = (rdev->sb_size | bmask) + 1; if (minor_version - && rdev->data_offset < sb_offset + (rdev->sb_size/512)) + && rdev->data_offset < sb_start + (rdev->sb_size/512)) return -EINVAL; if (sb->level == cpu_to_le32(LEVEL_MULTIPATH)) @@ -1163,7 +1159,7 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) if (minor_version) rdev->size = ((rdev->bdev->bd_inode->i_size>>9) - le64_to_cpu(sb->data_offset)) / 2; else - rdev->size = rdev->sb_offset; + rdev->size = rdev->sb_start / 2; if (rdev->size < le64_to_cpu(sb->data_size)/2) return -EINVAL; rdev->size = le64_to_cpu(sb->data_size)/2; @@ -1350,7 +1346,7 @@ super_1_rdev_size_change(mdk_rdev_t *rdev, unsigned long long size) if (size && size < rdev->mddev->size) return 0; /* component must fit device */ size *= 2; /* convert to sectors */ - if (rdev->sb_offset < rdev->data_offset/2) { + if (rdev->sb_start < rdev->data_offset) { /* minor versions 1 and 2; superblock before data */ max_size = (rdev->bdev->bd_inode->i_size >> 9); max_size -= rdev->data_offset; @@ -1361,19 +1357,19 @@ super_1_rdev_size_change(mdk_rdev_t *rdev, unsigned long long size) return 0; } else { /* minor version 0; superblock after data */ - sector_t sb_offset; - sb_offset = (rdev->bdev->bd_inode->i_size >> 9) - 8*2; - sb_offset &= ~(sector_t)(4*2 - 1); - max_size = rdev->size*2 + sb_offset - rdev->sb_offset*2; + sector_t sb_start; + sb_start = (rdev->bdev->bd_inode->i_size >> 9) - 8*2; + sb_start &= ~(sector_t)(4*2 - 1); + max_size = rdev->size*2 + sb_start - rdev->sb_start; if (!size || size > max_size) size = max_size; - rdev->sb_offset = sb_offset/2; + rdev->sb_start = sb_start; } sb = (struct mdp_superblock_1 *) page_address(rdev->sb_page); sb->data_size = cpu_to_le64(size); - sb->super_offset = rdev->sb_offset*2; + sb->super_offset = rdev->sb_start; sb->sb_csum = calc_sb_1_csum(sb); - md_super_write(rdev->mddev, rdev, rdev->sb_offset << 1, rdev->sb_size, + md_super_write(rdev->mddev, rdev, rdev->sb_start, rdev->sb_size, rdev->sb_page); md_super_wait(rdev->mddev); return size/2; /* kB for sysfs */ @@ -1810,11 +1806,11 @@ repeat: dprintk("%s ", bdevname(rdev->bdev,b)); if (!test_bit(Faulty, &rdev->flags)) { md_super_write(mddev,rdev, - rdev->sb_offset<<1, rdev->sb_size, + rdev->sb_start, rdev->sb_size, rdev->sb_page); dprintk(KERN_INFO "(write) %s's sb offset: %llu\n", bdevname(rdev->bdev,b), - (unsigned long long)rdev->sb_offset); + (unsigned long long)rdev->sb_start); rdev->sb_events = mddev->events; } else @@ -3577,16 +3573,16 @@ static int do_md_run(mddev_t * mddev) * We don't want the data to overlap the metadata, * Internal Bitmap issues has handled elsewhere. */ - if (rdev->data_offset < rdev->sb_offset) { + if (rdev->data_offset < rdev->sb_start) { if (mddev->size && rdev->data_offset + mddev->size*2 - > rdev->sb_offset*2) { + > rdev->sb_start) { printk("md: %s: data overlaps metadata\n", mdname(mddev)); return -EINVAL; } } else { - if (rdev->sb_offset*2 + rdev->sb_size/512 + if (rdev->sb_start + rdev->sb_size/512 > rdev->data_offset) { printk("md: %s: metadata overlaps data\n", mdname(mddev)); @@ -4355,9 +4351,9 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info) if (!mddev->persistent) { printk(KERN_INFO "md: nonpersistent superblock ...\n"); - rdev->sb_offset = rdev->bdev->bd_inode->i_size >> BLOCK_SIZE_BITS; + rdev->sb_start = rdev->bdev->bd_inode->i_size / 512; } else - rdev->sb_offset = calc_dev_sboffset(rdev->bdev) / 2; + rdev->sb_start = calc_dev_sboffset(rdev->bdev); rdev->size = calc_num_sectors(rdev, mddev->chunk_size) / 2; err = bind_rdev_to_array(rdev, mddev); @@ -4424,10 +4420,9 @@ static int hot_add_disk(mddev_t * mddev, dev_t dev) } if (mddev->persistent) - rdev->sb_offset = calc_dev_sboffset(rdev->bdev) / 2; + rdev->sb_start = calc_dev_sboffset(rdev->bdev); else - rdev->sb_offset = - rdev->bdev->bd_inode->i_size >> BLOCK_SIZE_BITS; + rdev->sb_start = rdev->bdev->bd_inode->i_size / 512; rdev->size = calc_num_sectors(rdev, mddev->chunk_size) / 2; @@ -4628,7 +4623,7 @@ static int update_size(mddev_t *mddev, sector_t num_sectors) * linear and raid0 always use whatever space is available. We can only * consider changing this number if no resync or reconstruction is * happening, and if the new size is acceptable. It must fit before the - * sb_offset or, if that is Date: Fri, 11 Jul 2008 22:02:23 +1000 Subject: md: Remove some unused macros. Signed-off-by: Andre Noll Signed-off-by: Neil Brown --- include/linux/raid/md_p.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/raid/md_p.h b/include/linux/raid/md_p.h index 3f2cd98c508b..8b4de4a41ff1 100644 --- a/include/linux/raid/md_p.h +++ b/include/linux/raid/md_p.h @@ -43,14 +43,11 @@ */ #define MD_RESERVED_BYTES (64 * 1024) #define MD_RESERVED_SECTORS (MD_RESERVED_BYTES / 512) -#define MD_RESERVED_BLOCKS (MD_RESERVED_BYTES / BLOCK_SIZE) #define MD_NEW_SIZE_SECTORS(x) ((x & ~(MD_RESERVED_SECTORS - 1)) - MD_RESERVED_SECTORS) -#define MD_NEW_SIZE_BLOCKS(x) ((x & ~(MD_RESERVED_BLOCKS - 1)) - MD_RESERVED_BLOCKS) #define MD_SB_BYTES 4096 #define MD_SB_WORDS (MD_SB_BYTES / 4) -#define MD_SB_BLOCKS (MD_SB_BYTES / BLOCK_SIZE) #define MD_SB_SECTORS (MD_SB_BYTES / 512) /* -- cgit v1.2.3 From 5b5d0e704880addfd979c262e6441f126708539c Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Sun, 13 Jul 2008 11:51:40 +0100 Subject: dccp: Upgrade NDP count from 3 to 6 bytes RFC 4340, 7.7 specifies up to 6 bytes for the NDP Count option, whereas the code is currently limited to up to 3 bytes. This seems to be a relict of an earlier draft version and is brought up to date by the patch. Signed-off-by: Gerrit Renker --- include/linux/dccp.h | 6 ++---- net/dccp/ccids/ccid3.c | 2 +- net/dccp/ccids/lib/packet_history.c | 6 +++--- net/dccp/ccids/lib/packet_history.h | 6 +++--- net/dccp/options.c | 14 ++++++++------ 5 files changed, 17 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dccp.h b/include/linux/dccp.h index aa0737019e37..6080449fbec9 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -364,8 +364,6 @@ static inline unsigned int dccp_hdr_len(const struct sk_buff *skb) /* FIXME: for now we're default to 1 but it should really be 0 */ #define DCCPF_INITIAL_SEND_NDP_COUNT 1 -#define DCCP_NDP_LIMIT 0xFFFFFF - /** * struct dccp_minisock - Minimal DCCP connection representation * @@ -437,7 +435,7 @@ extern int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq, struct sk_buff *skb); struct dccp_options_received { - u32 dccpor_ndp; /* only 24 bits */ + u64 dccpor_ndp:48; u32 dccpor_timestamp; u32 dccpor_timestamp_echo; u32 dccpor_elapsed_time; @@ -533,7 +531,7 @@ struct dccp_sock { __u16 dccps_r_ack_ratio; __u16 dccps_pcslen; __u16 dccps_pcrlen; - unsigned long dccps_ndp_count; + __u64 dccps_ndp_count:48; unsigned long dccps_rate_last; struct dccp_minisock dccps_minisock; struct dccp_ackvec *dccps_hc_rx_ackvec; diff --git a/net/dccp/ccids/ccid3.c b/net/dccp/ccids/ccid3.c index a1929f33d703..523db262c18f 100644 --- a/net/dccp/ccids/ccid3.c +++ b/net/dccp/ccids/ccid3.c @@ -794,7 +794,7 @@ static void ccid3_hc_rx_packet_recv(struct sock *sk, struct sk_buff *skb) { struct ccid3_hc_rx_sock *hcrx = ccid3_hc_rx_sk(sk); enum ccid3_fback_type do_feedback = CCID3_FBACK_NONE; - const u32 ndp = dccp_sk(sk)->dccps_options_received.dccpor_ndp; + const u64 ndp = dccp_sk(sk)->dccps_options_received.dccpor_ndp; const bool is_data_packet = dccp_data_packet(skb); if (unlikely(hcrx->ccid3hcrx_state == TFRC_RSTATE_NO_DATA)) { diff --git a/net/dccp/ccids/lib/packet_history.c b/net/dccp/ccids/lib/packet_history.c index 8b5c41ec7ee1..712930564c38 100644 --- a/net/dccp/ccids/lib/packet_history.c +++ b/net/dccp/ccids/lib/packet_history.c @@ -153,7 +153,7 @@ void tfrc_rx_packet_history_exit(void) static inline void tfrc_rx_hist_entry_from_skb(struct tfrc_rx_hist_entry *entry, const struct sk_buff *skb, - const u32 ndp) + const u64 ndp) { const struct dccp_hdr *dh = dccp_hdr(skb); @@ -166,7 +166,7 @@ static inline void tfrc_rx_hist_entry_from_skb(struct tfrc_rx_hist_entry *entry, void tfrc_rx_hist_add_packet(struct tfrc_rx_hist *h, const struct sk_buff *skb, - const u32 ndp) + const u64 ndp) { struct tfrc_rx_hist_entry *entry = tfrc_rx_hist_last_rcv(h); @@ -356,7 +356,7 @@ static void __three_after_loss(struct tfrc_rx_hist *h) */ int tfrc_rx_handle_loss(struct tfrc_rx_hist *h, struct tfrc_loss_hist *lh, - struct sk_buff *skb, u32 ndp, + struct sk_buff *skb, const u64 ndp, u32 (*calc_first_li)(struct sock *), struct sock *sk) { int is_new_loss = 0; diff --git a/net/dccp/ccids/lib/packet_history.h b/net/dccp/ccids/lib/packet_history.h index c7eeda49cb20..6976156cda3c 100644 --- a/net/dccp/ccids/lib/packet_history.h +++ b/net/dccp/ccids/lib/packet_history.h @@ -64,7 +64,7 @@ struct tfrc_rx_hist_entry { u64 tfrchrx_seqno:48, tfrchrx_ccval:4, tfrchrx_type:4; - u32 tfrchrx_ndp; /* In fact it is from 8 to 24 bits */ + u64 tfrchrx_ndp:48; ktime_t tfrchrx_tstamp; }; @@ -145,14 +145,14 @@ static inline int tfrc_rx_hist_new_loss_indicated(struct tfrc_rx_hist *h, } extern void tfrc_rx_hist_add_packet(struct tfrc_rx_hist *h, - const struct sk_buff *skb, const u32 ndp); + const struct sk_buff *skb, const u64 ndp); extern int tfrc_rx_hist_duplicate(struct tfrc_rx_hist *h, struct sk_buff *skb); struct tfrc_loss_hist; extern int tfrc_rx_handle_loss(struct tfrc_rx_hist *h, struct tfrc_loss_hist *lh, - struct sk_buff *skb, u32 ndp, + struct sk_buff *skb, const u64 ndp, u32 (*first_li)(struct sock *sk), struct sock *sk); extern u32 tfrc_rx_hist_sample_rtt(struct tfrc_rx_hist *h, diff --git a/net/dccp/options.c b/net/dccp/options.c index 43bc24e761d0..dc7c158a2f4b 100644 --- a/net/dccp/options.c +++ b/net/dccp/options.c @@ -124,12 +124,12 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq, mandatory = 1; break; case DCCPO_NDP_COUNT: - if (len > 3) + if (len > 6) goto out_invalid_option; opt_recv->dccpor_ndp = dccp_decode_value_var(value, len); - dccp_pr_debug("%s rx opt: NDP count=%d\n", dccp_role(sk), - opt_recv->dccpor_ndp); + dccp_pr_debug("%s opt: NDP count=%llu\n", dccp_role(sk), + (unsigned long long)opt_recv->dccpor_ndp); break; case DCCPO_CHANGE_L: /* fall through */ @@ -307,9 +307,11 @@ static void dccp_encode_value_var(const u32 value, unsigned char *to, *to++ = (value & 0xFF); } -static inline int dccp_ndp_len(const int ndp) +static inline u8 dccp_ndp_len(const u64 ndp) { - return likely(ndp <= 0xFF) ? 1 : ndp <= 0xFFFF ? 2 : 3; + if (likely(ndp <= 0xFF)) + return 1; + return likely(ndp <= USHORT_MAX) ? 2 : (ndp <= UINT_MAX ? 4 : 6); } int dccp_insert_option(struct sock *sk, struct sk_buff *skb, @@ -336,7 +338,7 @@ EXPORT_SYMBOL_GPL(dccp_insert_option); static int dccp_insert_option_ndp(struct sock *sk, struct sk_buff *skb) { struct dccp_sock *dp = dccp_sk(sk); - int ndp = dp->dccps_ndp_count; + u64 ndp = dp->dccps_ndp_count; if (dccp_non_data_packet(skb)) ++dp->dccps_ndp_count; -- cgit v1.2.3 From afc1246f917c664b0df98b3c22fa62db74d2ca33 Mon Sep 17 00:00:00 2001 From: Richard Kennedy Date: Fri, 11 Jul 2008 17:20:49 -0700 Subject: file lock: reorder struct file_lock to save space on 64 bit builds Reduce sizeof struct file_lock by 8 on 64 bit builds allowing +1 objects per slab in the file_lock_cache Signed-off-by: Richard Kennedy Signed-off-by: Andrew Morton Signed-off-by: J. Bruce Fields --- include/linux/fs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/fs.h b/include/linux/fs.h index 7c1080826832..87f89bd0f6ee 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -917,12 +917,12 @@ struct file_lock { struct list_head fl_link; /* doubly linked list of all locks */ struct list_head fl_block; /* circular list of blocked processes */ fl_owner_t fl_owner; + unsigned char fl_flags; + unsigned char fl_type; unsigned int fl_pid; struct pid *fl_nspid; wait_queue_head_t fl_wait; struct file *fl_file; - unsigned char fl_flags; - unsigned char fl_type; loff_t fl_start; loff_t fl_end; -- cgit v1.2.3 From 9c0c7a429a0cf02c2ac1998d5cf4c26f6be5c989 Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Wed, 9 Jul 2008 15:48:45 +0200 Subject: ssb: Include dma-mapping.h ssb.h implements DMA mapping functions, so it should include dma-mapping.h. This fixes compile failures on certain architectures. Reported-by: Stephen Rothwell Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- include/linux/ssb/ssb.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h index 0fe5a0ded3ea..4bf8cade9dbc 100644 --- a/include/linux/ssb/ssb.h +++ b/include/linux/ssb/ssb.h @@ -7,6 +7,7 @@ #include #include #include +#include #include -- cgit v1.2.3 From 11c3b79218390a139f2d474ee1e983a672d5839a Mon Sep 17 00:00:00 2001 From: Joel Becker Date: Thu, 12 Jun 2008 14:00:18 -0700 Subject: configfs: Allow ->make_item() and ->make_group() to return detailed errors. The configfs operations ->make_item() and ->make_group() currently return a new item/group. A return of NULL signifies an error. Because of this, -ENOMEM is the only return code bubbled up the stack. Multiple folks have requested the ability to return specific error codes when these operations fail. This patch adds that ability by changing the ->make_item/group() ops to return an int. Also updated are the in-kernel users of configfs. Signed-off-by: Joel Becker --- Documentation/filesystems/configfs/configfs.txt | 10 +++-- .../filesystems/configfs/configfs_example.c | 14 ++++--- drivers/net/netconsole.c | 10 +++-- fs/configfs/dir.c | 13 +++---- fs/dlm/config.c | 45 ++++++++++++++-------- fs/ocfs2/cluster/heartbeat.c | 17 ++++---- fs/ocfs2/cluster/nodemanager.c | 45 ++++++++++++++-------- include/linux/configfs.h | 4 +- 8 files changed, 94 insertions(+), 64 deletions(-) (limited to 'include/linux') diff --git a/Documentation/filesystems/configfs/configfs.txt b/Documentation/filesystems/configfs/configfs.txt index 44c97e6accb2..15838d706ea2 100644 --- a/Documentation/filesystems/configfs/configfs.txt +++ b/Documentation/filesystems/configfs/configfs.txt @@ -233,10 +233,12 @@ accomplished via the group operations specified on the group's config_item_type. struct configfs_group_operations { - struct config_item *(*make_item)(struct config_group *group, - const char *name); - struct config_group *(*make_group)(struct config_group *group, - const char *name); + int (*make_item)(struct config_group *group, + const char *name, + struct config_item **new_item); + int (*make_group)(struct config_group *group, + const char *name, + struct config_group **new_group); int (*commit_item)(struct config_item *item); void (*disconnect_notify)(struct config_group *group, struct config_item *item); diff --git a/Documentation/filesystems/configfs/configfs_example.c b/Documentation/filesystems/configfs/configfs_example.c index 25151fd5c2c6..0b422acd470c 100644 --- a/Documentation/filesystems/configfs/configfs_example.c +++ b/Documentation/filesystems/configfs/configfs_example.c @@ -273,13 +273,13 @@ static inline struct simple_children *to_simple_children(struct config_item *ite return item ? container_of(to_config_group(item), struct simple_children, group) : NULL; } -static struct config_item *simple_children_make_item(struct config_group *group, const char *name) +static int simple_children_make_item(struct config_group *group, const char *name, struct config_item **new_item) { struct simple_child *simple_child; simple_child = kzalloc(sizeof(struct simple_child), GFP_KERNEL); if (!simple_child) - return NULL; + return -ENOMEM; config_item_init_type_name(&simple_child->item, name, @@ -287,7 +287,8 @@ static struct config_item *simple_children_make_item(struct config_group *group, simple_child->storeme = 0; - return &simple_child->item; + *new_item = &simple_child->item; + return 0; } static struct configfs_attribute simple_children_attr_description = { @@ -359,20 +360,21 @@ static struct configfs_subsystem simple_children_subsys = { * children of its own. */ -static struct config_group *group_children_make_group(struct config_group *group, const char *name) +static int group_children_make_group(struct config_group *group, const char *name, struct config_group **new_group) { struct simple_children *simple_children; simple_children = kzalloc(sizeof(struct simple_children), GFP_KERNEL); if (!simple_children) - return NULL; + return -ENOMEM; config_group_init_type_name(&simple_children->group, name, &simple_children_type); - return &simple_children->group; + *new_group = &simple_children->group; + return 0; } static struct configfs_attribute group_children_attr_description = { diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index 665341e43055..387a13395015 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -585,8 +585,9 @@ static struct config_item_type netconsole_target_type = { * Group operations and type for netconsole_subsys. */ -static struct config_item *make_netconsole_target(struct config_group *group, - const char *name) +static int make_netconsole_target(struct config_group *group, + const char *name, + struct config_item **new_item) { unsigned long flags; struct netconsole_target *nt; @@ -598,7 +599,7 @@ static struct config_item *make_netconsole_target(struct config_group *group, nt = kzalloc(sizeof(*nt), GFP_KERNEL); if (!nt) { printk(KERN_ERR "netconsole: failed to allocate memory\n"); - return NULL; + return -ENOMEM; } nt->np.name = "netconsole"; @@ -615,7 +616,8 @@ static struct config_item *make_netconsole_target(struct config_group *group, list_add(&nt->list, &target_list); spin_unlock_irqrestore(&target_list_lock, flags); - return &nt->item; + *new_item = &nt->item; + return 0; } static void drop_netconsole_target(struct config_group *group, diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 614e382a6049..0e64312a084c 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -1073,25 +1073,24 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) group = NULL; item = NULL; if (type->ct_group_ops->make_group) { - group = type->ct_group_ops->make_group(to_config_group(parent_item), name); - if (group) { + ret = type->ct_group_ops->make_group(to_config_group(parent_item), name, &group); + if (!ret) { link_group(to_config_group(parent_item), group); item = &group->cg_item; } } else { - item = type->ct_group_ops->make_item(to_config_group(parent_item), name); - if (item) + ret = type->ct_group_ops->make_item(to_config_group(parent_item), name, &item); + if (!ret) link_obj(parent_item, item); } mutex_unlock(&subsys->su_mutex); kfree(name); - if (!item) { + if (ret) { /* - * If item == NULL, then link_obj() was never called. + * If ret != 0, then link_obj() was never called. * There are no extra references to clean up. */ - ret = -ENOMEM; goto out_put; } diff --git a/fs/dlm/config.c b/fs/dlm/config.c index eac23bd288b2..492d8caaaf25 100644 --- a/fs/dlm/config.c +++ b/fs/dlm/config.c @@ -41,16 +41,20 @@ struct comm; struct nodes; struct node; -static struct config_group *make_cluster(struct config_group *, const char *); +static int make_cluster(struct config_group *, const char *, + struct config_group **); static void drop_cluster(struct config_group *, struct config_item *); static void release_cluster(struct config_item *); -static struct config_group *make_space(struct config_group *, const char *); +static int make_space(struct config_group *, const char *, + struct config_group **); static void drop_space(struct config_group *, struct config_item *); static void release_space(struct config_item *); -static struct config_item *make_comm(struct config_group *, const char *); +static int make_comm(struct config_group *, const char *, + struct config_item **); static void drop_comm(struct config_group *, struct config_item *); static void release_comm(struct config_item *); -static struct config_item *make_node(struct config_group *, const char *); +static int make_node(struct config_group *, const char *, + struct config_item **); static void drop_node(struct config_group *, struct config_item *); static void release_node(struct config_item *); @@ -392,8 +396,8 @@ static struct node *to_node(struct config_item *i) return i ? container_of(i, struct node, item) : NULL; } -static struct config_group *make_cluster(struct config_group *g, - const char *name) +static int make_cluster(struct config_group *g, const char *name, + struct config_group **new_g) { struct cluster *cl = NULL; struct spaces *sps = NULL; @@ -431,14 +435,15 @@ static struct config_group *make_cluster(struct config_group *g, space_list = &sps->ss_group; comm_list = &cms->cs_group; - return &cl->group; + *new_g = &cl->group; + return 0; fail: kfree(cl); kfree(gps); kfree(sps); kfree(cms); - return NULL; + return -ENOMEM; } static void drop_cluster(struct config_group *g, struct config_item *i) @@ -466,7 +471,8 @@ static void release_cluster(struct config_item *i) kfree(cl); } -static struct config_group *make_space(struct config_group *g, const char *name) +static int make_space(struct config_group *g, const char *name, + struct config_group **new_g) { struct space *sp = NULL; struct nodes *nds = NULL; @@ -489,13 +495,14 @@ static struct config_group *make_space(struct config_group *g, const char *name) INIT_LIST_HEAD(&sp->members); mutex_init(&sp->members_lock); sp->members_count = 0; - return &sp->group; + *new_g = &sp->group; + return 0; fail: kfree(sp); kfree(gps); kfree(nds); - return NULL; + return -ENOMEM; } static void drop_space(struct config_group *g, struct config_item *i) @@ -522,19 +529,21 @@ static void release_space(struct config_item *i) kfree(sp); } -static struct config_item *make_comm(struct config_group *g, const char *name) +static int make_comm(struct config_group *g, const char *name, + struct config_item **new_i) { struct comm *cm; cm = kzalloc(sizeof(struct comm), GFP_KERNEL); if (!cm) - return NULL; + return -ENOMEM; config_item_init_type_name(&cm->item, name, &comm_type); cm->nodeid = -1; cm->local = 0; cm->addr_count = 0; - return &cm->item; + *new_i = &cm->item; + return 0; } static void drop_comm(struct config_group *g, struct config_item *i) @@ -554,14 +563,15 @@ static void release_comm(struct config_item *i) kfree(cm); } -static struct config_item *make_node(struct config_group *g, const char *name) +static int make_node(struct config_group *g, const char *name, + struct config_item **new_i) { struct space *sp = to_space(g->cg_item.ci_parent); struct node *nd; nd = kzalloc(sizeof(struct node), GFP_KERNEL); if (!nd) - return NULL; + return -ENOMEM; config_item_init_type_name(&nd->item, name, &node_type); nd->nodeid = -1; @@ -573,7 +583,8 @@ static struct config_item *make_node(struct config_group *g, const char *name) sp->members_count++; mutex_unlock(&sp->members_lock); - return &nd->item; + *new_i = &nd->item; + return 0; } static void drop_node(struct config_group *g, struct config_item *i) diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index f02ccb34604d..443d108211ab 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -1489,25 +1489,28 @@ static struct o2hb_heartbeat_group *to_o2hb_heartbeat_group(struct config_group : NULL; } -static struct config_item *o2hb_heartbeat_group_make_item(struct config_group *group, - const char *name) +static int o2hb_heartbeat_group_make_item(struct config_group *group, + const char *name, + struct config_item **new_item) { struct o2hb_region *reg = NULL; - struct config_item *ret = NULL; + int ret = 0; reg = kzalloc(sizeof(struct o2hb_region), GFP_KERNEL); - if (reg == NULL) - goto out; /* ENOMEM */ + if (reg == NULL) { + ret = -ENOMEM; + goto out; + } config_item_init_type_name(®->hr_item, name, &o2hb_region_type); - ret = ®->hr_item; + *new_item = ®->hr_item; spin_lock(&o2hb_live_lock); list_add_tail(®->hr_all_item, &o2hb_all_regions); spin_unlock(&o2hb_live_lock); out: - if (ret == NULL) + if (ret) kfree(reg); return ret; diff --git a/fs/ocfs2/cluster/nodemanager.c b/fs/ocfs2/cluster/nodemanager.c index cfdb08b484ed..b364b7052e46 100644 --- a/fs/ocfs2/cluster/nodemanager.c +++ b/fs/ocfs2/cluster/nodemanager.c @@ -644,27 +644,32 @@ out: return ret; } -static struct config_item *o2nm_node_group_make_item(struct config_group *group, - const char *name) +static int o2nm_node_group_make_item(struct config_group *group, + const char *name, + struct config_item **new_item) { struct o2nm_node *node = NULL; - struct config_item *ret = NULL; + int ret = 0; - if (strlen(name) > O2NM_MAX_NAME_LEN) - goto out; /* ENAMETOOLONG */ + if (strlen(name) > O2NM_MAX_NAME_LEN) { + ret = -ENAMETOOLONG; + goto out; + } node = kzalloc(sizeof(struct o2nm_node), GFP_KERNEL); - if (node == NULL) - goto out; /* ENOMEM */ + if (node == NULL) { + ret = -ENOMEM; + goto out; + } strcpy(node->nd_name, name); /* use item.ci_namebuf instead? */ config_item_init_type_name(&node->nd_item, name, &o2nm_node_type); spin_lock_init(&node->nd_lock); - ret = &node->nd_item; + *new_item = &node->nd_item; out: - if (ret == NULL) + if (ret) kfree(node); return ret; @@ -751,25 +756,31 @@ static struct o2nm_cluster_group *to_o2nm_cluster_group(struct config_group *gro } #endif -static struct config_group *o2nm_cluster_group_make_group(struct config_group *group, - const char *name) +static int o2nm_cluster_group_make_group(struct config_group *group, + const char *name, + struct config_group **new_group) { struct o2nm_cluster *cluster = NULL; struct o2nm_node_group *ns = NULL; - struct config_group *o2hb_group = NULL, *ret = NULL; + struct config_group *o2hb_group = NULL; void *defs = NULL; + int ret = 0; /* this runs under the parent dir's i_mutex; there can be only * one caller in here at a time */ - if (o2nm_single_cluster) - goto out; /* ENOSPC */ + if (o2nm_single_cluster) { + ret = -ENOSPC; + goto out; + } cluster = kzalloc(sizeof(struct o2nm_cluster), GFP_KERNEL); ns = kzalloc(sizeof(struct o2nm_node_group), GFP_KERNEL); defs = kcalloc(3, sizeof(struct config_group *), GFP_KERNEL); o2hb_group = o2hb_alloc_hb_set(); - if (cluster == NULL || ns == NULL || o2hb_group == NULL || defs == NULL) + if (cluster == NULL || ns == NULL || o2hb_group == NULL || defs == NULL) { + ret = -ENOMEM; goto out; + } config_group_init_type_name(&cluster->cl_group, name, &o2nm_cluster_type); @@ -786,11 +797,11 @@ static struct config_group *o2nm_cluster_group_make_group(struct config_group *g cluster->cl_idle_timeout_ms = O2NET_IDLE_TIMEOUT_MS_DEFAULT; cluster->cl_keepalive_delay_ms = O2NET_KEEPALIVE_DELAY_MS_DEFAULT; - ret = &cluster->cl_group; + *new_group = &cluster->cl_group; o2nm_single_cluster = cluster; out: - if (ret == NULL) { + if (ret) { kfree(cluster); kfree(ns); o2hb_free_hb_set(o2hb_group); diff --git a/include/linux/configfs.h b/include/linux/configfs.h index 3ae65b1bf90f..0488f937634a 100644 --- a/include/linux/configfs.h +++ b/include/linux/configfs.h @@ -165,8 +165,8 @@ struct configfs_item_operations { }; struct configfs_group_operations { - struct config_item *(*make_item)(struct config_group *group, const char *name); - struct config_group *(*make_group)(struct config_group *group, const char *name); + int (*make_item)(struct config_group *group, const char *name, struct config_item **new_item); + int (*make_group)(struct config_group *group, const char *name, struct config_group **new_group); int (*commit_item)(struct config_item *item); void (*disconnect_notify)(struct config_group *group, struct config_item *item); void (*drop_item)(struct config_group *group, struct config_item *item); -- cgit v1.2.3 From c300bd2fb583afb6d68804afd38bc90b31310d95 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Thu, 10 Jul 2008 02:16:44 +0200 Subject: PCI: include linux/pm_wakeup.h for device_set_wakeup_capable drivers/pci/pci.c needs pm_wakeup.h since it uses device_set_wakup_capable(). The latter also needs to be stubbed out for !CONFIG_PM. Signed-off-by: Stephen Rothwell Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 1 + include/linux/pm_wakeup.h | 2 ++ 2 files changed, 3 insertions(+) (limited to 'include/linux') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index ace518116cc5..44a46c92b721 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -17,6 +17,7 @@ #include #include #include +#include #include /* isa_dma_bridge_buggy */ #include "pci.h" diff --git a/include/linux/pm_wakeup.h b/include/linux/pm_wakeup.h index 3af0c8d05cdc..0aae7776185e 100644 --- a/include/linux/pm_wakeup.h +++ b/include/linux/pm_wakeup.h @@ -63,6 +63,8 @@ static inline void device_init_wakeup(struct device *dev, int val) dev->power.can_wakeup = !!val; } +static inline void device_set_wakeup_capable(struct device *dev, int val) { } + static inline int device_can_wakeup(struct device *dev) { return dev->power.can_wakeup; -- cgit v1.2.3 From 72d9794f444734af56ef12833b496326643e2964 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Mon, 14 Jul 2008 20:36:32 -0700 Subject: net-sched: cls_flow: add perturbation support Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/pkt_cls.h | 1 + net/sched/cls_flow.c | 52 ++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 44 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pkt_cls.h b/include/linux/pkt_cls.h index 99efbed81fa2..7cf7824df778 100644 --- a/include/linux/pkt_cls.h +++ b/include/linux/pkt_cls.h @@ -374,6 +374,7 @@ enum TCA_FLOW_ACT, TCA_FLOW_POLICE, TCA_FLOW_EMATCHES, + TCA_FLOW_PERTURB, __TCA_FLOW_MAX }; diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index 971b867e0484..8f63a1a94014 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c @@ -36,6 +36,8 @@ struct flow_filter { struct list_head list; struct tcf_exts exts; struct tcf_ematch_tree ematches; + struct timer_list perturb_timer; + u32 perturb_period; u32 handle; u32 nkeys; @@ -47,11 +49,9 @@ struct flow_filter { u32 addend; u32 divisor; u32 baseclass; + u32 hashrnd; }; -static u32 flow_hashrnd __read_mostly; -static int flow_hashrnd_initted __read_mostly; - static const struct tcf_ext_map flow_ext_map = { .action = TCA_FLOW_ACT, .police = TCA_FLOW_POLICE, @@ -348,7 +348,7 @@ static int flow_classify(struct sk_buff *skb, struct tcf_proto *tp, } if (f->mode == FLOW_MODE_HASH) - classid = jhash2(keys, f->nkeys, flow_hashrnd); + classid = jhash2(keys, f->nkeys, f->hashrnd); else { classid = keys[0]; classid = (classid & f->mask) ^ f->xor; @@ -369,6 +369,15 @@ static int flow_classify(struct sk_buff *skb, struct tcf_proto *tp, return -1; } +static void flow_perturbation(unsigned long arg) +{ + struct flow_filter *f = (struct flow_filter *)arg; + + get_random_bytes(&f->hashrnd, 4); + if (f->perturb_period) + mod_timer(&f->perturb_timer, jiffies + f->perturb_period); +} + static const struct nla_policy flow_policy[TCA_FLOW_MAX + 1] = { [TCA_FLOW_KEYS] = { .type = NLA_U32 }, [TCA_FLOW_MODE] = { .type = NLA_U32 }, @@ -381,6 +390,7 @@ static const struct nla_policy flow_policy[TCA_FLOW_MAX + 1] = { [TCA_FLOW_ACT] = { .type = NLA_NESTED }, [TCA_FLOW_POLICE] = { .type = NLA_NESTED }, [TCA_FLOW_EMATCHES] = { .type = NLA_NESTED }, + [TCA_FLOW_PERTURB] = { .type = NLA_U32 }, }; static int flow_change(struct tcf_proto *tp, unsigned long base, @@ -394,6 +404,7 @@ static int flow_change(struct tcf_proto *tp, unsigned long base, struct tcf_exts e; struct tcf_ematch_tree t; unsigned int nkeys = 0; + unsigned int perturb_period = 0; u32 baseclass = 0; u32 keymask = 0; u32 mode; @@ -442,6 +453,14 @@ static int flow_change(struct tcf_proto *tp, unsigned long base, mode = nla_get_u32(tb[TCA_FLOW_MODE]); if (mode != FLOW_MODE_HASH && nkeys > 1) goto err2; + + if (mode == FLOW_MODE_HASH) + perturb_period = f->perturb_period; + if (tb[TCA_FLOW_PERTURB]) { + if (mode != FLOW_MODE_HASH) + goto err2; + perturb_period = nla_get_u32(tb[TCA_FLOW_PERTURB]) * HZ; + } } else { err = -EINVAL; if (!handle) @@ -455,6 +474,12 @@ static int flow_change(struct tcf_proto *tp, unsigned long base, if (mode != FLOW_MODE_HASH && nkeys > 1) goto err2; + if (tb[TCA_FLOW_PERTURB]) { + if (mode != FLOW_MODE_HASH) + goto err2; + perturb_period = nla_get_u32(tb[TCA_FLOW_PERTURB]) * HZ; + } + if (TC_H_MAJ(baseclass) == 0) baseclass = TC_H_MAKE(tp->q->handle, baseclass); if (TC_H_MIN(baseclass) == 0) @@ -467,6 +492,11 @@ static int flow_change(struct tcf_proto *tp, unsigned long base, f->handle = handle; f->mask = ~0U; + + get_random_bytes(&f->hashrnd, 4); + f->perturb_timer.function = flow_perturbation; + f->perturb_timer.data = (unsigned long)f; + init_timer_deferrable(&f->perturb_timer); } tcf_exts_change(tp, &f->exts, &e); @@ -495,6 +525,11 @@ static int flow_change(struct tcf_proto *tp, unsigned long base, if (baseclass) f->baseclass = baseclass; + f->perturb_period = perturb_period; + del_timer(&f->perturb_timer); + if (perturb_period) + mod_timer(&f->perturb_timer, jiffies + perturb_period); + if (*arg == 0) list_add_tail(&f->list, &head->filters); @@ -512,6 +547,7 @@ err1: static void flow_destroy_filter(struct tcf_proto *tp, struct flow_filter *f) { + del_timer_sync(&f->perturb_timer); tcf_exts_destroy(tp, &f->exts); tcf_em_tree_destroy(tp, &f->ematches); kfree(f); @@ -532,11 +568,6 @@ static int flow_init(struct tcf_proto *tp) { struct flow_head *head; - if (!flow_hashrnd_initted) { - get_random_bytes(&flow_hashrnd, 4); - flow_hashrnd_initted = 1; - } - head = kzalloc(sizeof(*head), GFP_KERNEL); if (head == NULL) return -ENOBUFS; @@ -605,6 +636,9 @@ static int flow_dump(struct tcf_proto *tp, unsigned long fh, if (f->baseclass) NLA_PUT_U32(skb, TCA_FLOW_BASECLASS, f->baseclass); + if (f->perturb_period) + NLA_PUT_U32(skb, TCA_FLOW_PERTURB, f->perturb_period / HZ); + if (tcf_exts_dump(skb, &f->exts, &flow_ext_map) < 0) goto nla_put_failure; #ifdef CONFIG_NET_EMATCH -- cgit v1.2.3 From f271b2cc78f09c93ccd00a2056d3237134bf994c Mon Sep 17 00:00:00 2001 From: Max Krasnyansky Date: Mon, 14 Jul 2008 22:18:19 -0700 Subject: tun: Fix/rewrite packet filtering logic Please see the following thread to get some context on this http://marc.info/?l=linux-netdev&m=121564433018903&w=2 Basically the issue is that current multi-cast filtering stuff in the TUN/TAP driver is seriously broken. Original patch went in without proper review and ACK. It was broken and confusing to start with and subsequent patches broke it completely. To give you an idea of what's broken here are some of the issues: - Very confusing comments throughout the code that imply that the character device is a network interface in its own right, and that packets are passed between the two nics. Which is completely wrong. - Wrong set of ioctls is used for setting up filters. They look like shortcuts for manipulating state of the tun/tap network interface but in reality manipulate the state of the TX filter. - ioctls that were originally used for setting address of the the TX filter got "fixed" and now set the address of the network interface itself. Which made filter totaly useless. - Filtering is done too late. Instead of filtering early on, to avoid unnecessary wakeups, filtering is done in the read() call. The list goes on and on :) So the patch cleans all that up. It introduces simple and clean interface for setting up TX filters (TUNSETTXFILTER + tun_filter spec) and does filtering before enqueuing the packets. TX filtering is useful in the scenarios where TAP is part of a bridge, in which case it gets all broadcast, multicast and potentially other packets when the bridge is learning. So for example Ethernet tunnelling app may want to setup TX filters to avoid tunnelling multicast traffic. QEMU and other hypervisors can push RX filtering that is currently done in the guest into the host context therefore saving wakeups and unnecessary data transfer. Signed-off-by: Max Krasnyansky Signed-off-by: David S. Miller --- drivers/net/tun.c | 316 ++++++++++++++++++++++++------------------------- include/linux/if_tun.h | 24 +++- 2 files changed, 174 insertions(+), 166 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 2693f883ecda..901551c8ca0e 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -18,15 +18,11 @@ /* * Changes: * - * Brian Braunstein 2007/03/23 - * Fixed hw address handling. Now net_device.dev_addr is kept consistent - * with tun.dev_addr when the address is set by this module. - * * Mike Kershaw 2005/08/14 * Add TUNSETLINK ioctl to set the link encapsulation * * Mark Smith - * Use random_ether_addr() for tap MAC address. + * Use random_ether_addr() for tap MAC address. * * Harald Roelle 2004/04/20 * Fixes in packet dropping, queue length setting and queue wakeup. @@ -83,9 +79,16 @@ static int debug; #define DBG1( a... ) #endif +#define FLT_EXACT_COUNT 8 +struct tap_filter { + unsigned int count; /* Number of addrs. Zero means disabled */ + u32 mask[2]; /* Mask of the hashed addrs */ + unsigned char addr[FLT_EXACT_COUNT][ETH_ALEN]; +}; + struct tun_struct { struct list_head list; - unsigned long flags; + unsigned int flags; int attached; uid_t owner; gid_t group; @@ -94,19 +97,119 @@ struct tun_struct { struct sk_buff_head readq; struct net_device *dev; + struct fasync_struct *fasync; - struct fasync_struct *fasync; - - unsigned long if_flags; - u8 dev_addr[ETH_ALEN]; - u32 chr_filter[2]; - u32 net_filter[2]; + struct tap_filter txflt; #ifdef TUN_DEBUG int debug; #endif }; +/* TAP filterting */ +static void addr_hash_set(u32 *mask, const u8 *addr) +{ + int n = ether_crc(ETH_ALEN, addr) >> 26; + mask[n >> 5] |= (1 << (n & 31)); +} + +static unsigned int addr_hash_test(const u32 *mask, const u8 *addr) +{ + int n = ether_crc(ETH_ALEN, addr) >> 26; + return mask[n >> 5] & (1 << (n & 31)); +} + +static int update_filter(struct tap_filter *filter, void __user *arg) +{ + struct { u8 u[ETH_ALEN]; } *addr; + struct tun_filter uf; + int err, alen, n, nexact; + + if (copy_from_user(&uf, arg, sizeof(uf))) + return -EFAULT; + + if (!uf.count) { + /* Disabled */ + filter->count = 0; + return 0; + } + + alen = ETH_ALEN * uf.count; + addr = kmalloc(alen, GFP_KERNEL); + if (!addr) + return -ENOMEM; + + if (copy_from_user(addr, arg + sizeof(uf), alen)) { + err = -EFAULT; + goto done; + } + + /* The filter is updated without holding any locks. Which is + * perfectly safe. We disable it first and in the worst + * case we'll accept a few undesired packets. */ + filter->count = 0; + wmb(); + + /* Use first set of addresses as an exact filter */ + for (n = 0; n < uf.count && n < FLT_EXACT_COUNT; n++) + memcpy(filter->addr[n], addr[n].u, ETH_ALEN); + + nexact = n; + + /* The rest is hashed */ + memset(filter->mask, 0, sizeof(filter->mask)); + for (; n < uf.count; n++) + addr_hash_set(filter->mask, addr[n].u); + + /* For ALLMULTI just set the mask to all ones. + * This overrides the mask populated above. */ + if ((uf.flags & TUN_FLT_ALLMULTI)) + memset(filter->mask, ~0, sizeof(filter->mask)); + + /* Now enable the filter */ + wmb(); + filter->count = nexact; + + /* Return the number of exact filters */ + err = nexact; + +done: + kfree(addr); + return err; +} + +/* Returns: 0 - drop, !=0 - accept */ +static int run_filter(struct tap_filter *filter, const struct sk_buff *skb) +{ + /* Cannot use eth_hdr(skb) here because skb_mac_hdr() is incorrect + * at this point. */ + struct ethhdr *eh = (struct ethhdr *) skb->data; + int i; + + /* Exact match */ + for (i = 0; i < filter->count; i++) + if (!compare_ether_addr(eh->h_dest, filter->addr[i])) + return 1; + + /* Inexact match (multicast only) */ + if (is_multicast_ether_addr(eh->h_dest)) + return addr_hash_test(filter->mask, eh->h_dest); + + return 0; +} + +/* + * Checks whether the packet is accepted or not. + * Returns: 0 - drop, !=0 - accept + */ +static int check_filter(struct tap_filter *filter, const struct sk_buff *skb) +{ + if (!filter->count) + return 1; + + return run_filter(filter, skb); +} + /* Network device part of the driver */ static unsigned int tun_net_id; @@ -141,7 +244,12 @@ static int tun_net_xmit(struct sk_buff *skb, struct net_device *dev) if (!tun->attached) goto drop; - /* Packet dropping */ + /* Drop if the filter does not like it. + * This is a noop if the filter is disabled. + * Filter can be enabled only for the TAP devices. */ + if (!check_filter(&tun->txflt, skb)) + goto drop; + if (skb_queue_len(&tun->readq) >= dev->tx_queue_len) { if (!(tun->flags & TUN_ONE_QUEUE)) { /* Normal queueing mode. */ @@ -158,7 +266,7 @@ static int tun_net_xmit(struct sk_buff *skb, struct net_device *dev) } } - /* Queue packet */ + /* Enqueue packet */ skb_queue_tail(&tun->readq, skb); dev->trans_start = jiffies; @@ -174,41 +282,14 @@ drop: return 0; } -/** Add the specified Ethernet address to this multicast filter. */ -static void -add_multi(u32* filter, const u8* addr) -{ - int bit_nr = ether_crc(ETH_ALEN, addr) >> 26; - filter[bit_nr >> 5] |= 1 << (bit_nr & 31); -} - -/** Remove the specified Ethernet addres from this multicast filter. */ -static void -del_multi(u32* filter, const u8* addr) +static void tun_net_mclist(struct net_device *dev) { - int bit_nr = ether_crc(ETH_ALEN, addr) >> 26; - filter[bit_nr >> 5] &= ~(1 << (bit_nr & 31)); -} - -/** Update the list of multicast groups to which the network device belongs. - * This list is used to filter packets being sent from the character device to - * the network device. */ -static void -tun_net_mclist(struct net_device *dev) -{ - struct tun_struct *tun = netdev_priv(dev); - const struct dev_mc_list *mclist; - int i; - DECLARE_MAC_BUF(mac); - DBG(KERN_DEBUG "%s: tun_net_mclist: mc_count %d\n", - dev->name, dev->mc_count); - memset(tun->chr_filter, 0, sizeof tun->chr_filter); - for (i = 0, mclist = dev->mc_list; i < dev->mc_count && mclist != NULL; - i++, mclist = mclist->next) { - add_multi(tun->net_filter, mclist->dmi_addr); - DBG(KERN_DEBUG "%s: tun_net_mclist: %s\n", - dev->name, print_mac(mac, mclist->dmi_addr)); - } + /* + * This callback is supposed to deal with mc filter in + * _rx_ path and has nothing to do with the _tx_ path. + * In rx path we always accept everything userspace gives us. + */ + return; } #define MIN_MTU 68 @@ -244,13 +325,11 @@ static void tun_net_init(struct net_device *dev) case TUN_TAP_DEV: /* Ethernet TAP Device */ - dev->set_multicast_list = tun_net_mclist; - ether_setup(dev); - dev->change_mtu = tun_net_change_mtu; + dev->change_mtu = tun_net_change_mtu; + dev->set_multicast_list = tun_net_mclist; - /* random address already created for us by tun_set_iff, use it */ - memcpy(dev->dev_addr, tun->dev_addr, min(sizeof(tun->dev_addr), sizeof(dev->dev_addr)) ); + random_ether_addr(dev->dev_addr); dev->tx_queue_len = TUN_READQ_SIZE; /* We prefer our own queue length */ break; @@ -486,7 +565,6 @@ static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv, DECLARE_WAITQUEUE(wait, current); struct sk_buff *skb; ssize_t len, ret = 0; - DECLARE_MAC_BUF(mac); if (!tun) return -EBADFD; @@ -499,10 +577,6 @@ static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv, add_wait_queue(&tun->read_wait, &wait); while (len) { - const u8 ones[ ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - u8 addr[ ETH_ALEN]; - int bit_nr; - current->state = TASK_INTERRUPTIBLE; /* Read frames from the queue */ @@ -522,36 +596,9 @@ static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv, } netif_wake_queue(tun->dev); - /** Decide whether to accept this packet. This code is designed to - * behave identically to an Ethernet interface. Accept the packet if - * - we are promiscuous. - * - the packet is addressed to us. - * - the packet is broadcast. - * - the packet is multicast and - * - we are multicast promiscous. - * - we belong to the multicast group. - */ - skb_copy_from_linear_data(skb, addr, min_t(size_t, sizeof addr, - skb->len)); - bit_nr = ether_crc(sizeof addr, addr) >> 26; - if ((tun->if_flags & IFF_PROMISC) || - memcmp(addr, tun->dev_addr, sizeof addr) == 0 || - memcmp(addr, ones, sizeof addr) == 0 || - (((addr[0] == 1 && addr[1] == 0 && addr[2] == 0x5e) || - (addr[0] == 0x33 && addr[1] == 0x33)) && - ((tun->if_flags & IFF_ALLMULTI) || - (tun->chr_filter[bit_nr >> 5] & (1 << (bit_nr & 31)))))) { - DBG(KERN_DEBUG "%s: tun_chr_readv: accepted: %s\n", - tun->dev->name, print_mac(mac, addr)); - ret = tun_put_user(tun, skb, (struct iovec *) iv, len); - kfree_skb(skb); - break; - } else { - DBG(KERN_DEBUG "%s: tun_chr_readv: rejected: %s\n", - tun->dev->name, print_mac(mac, addr)); - kfree_skb(skb); - continue; - } + ret = tun_put_user(tun, skb, (struct iovec *) iv, len); + kfree_skb(skb); + break; } current->state = TASK_RUNNING; @@ -647,12 +694,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) tun = netdev_priv(dev); tun->dev = dev; tun->flags = flags; - /* Be promiscuous by default to maintain previous behaviour. */ - tun->if_flags = IFF_PROMISC; - /* Generate random Ethernet address. */ - *(__be16 *)tun->dev_addr = htons(0x00FF); - get_random_bytes(tun->dev_addr + sizeof(u16), 4); - memset(tun->chr_filter, 0, sizeof tun->chr_filter); + tun->txflt.count = 0; tun_net_init(dev); @@ -751,6 +793,7 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, struct tun_struct *tun = file->private_data; void __user* argp = (void __user*)arg; struct ifreq ifr; + int ret; DECLARE_MAC_BUF(mac); if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89) @@ -826,9 +869,6 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, break; case TUNSETLINK: - { - int ret; - /* Only allow setting the type when the interface is down */ rtnl_lock(); if (tun->dev->flags & IFF_UP) { @@ -842,94 +882,44 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, } rtnl_unlock(); return ret; - } #ifdef TUN_DEBUG case TUNSETDEBUG: tun->debug = arg; break; #endif - case TUNSETOFFLOAD: - { - int ret; rtnl_lock(); ret = set_offload(tun->dev, arg); rtnl_unlock(); return ret; - } - case SIOCGIFFLAGS: - ifr.ifr_flags = tun->if_flags; - if (copy_to_user( argp, &ifr, sizeof ifr)) - return -EFAULT; - return 0; - - case SIOCSIFFLAGS: - /** Set the character device's interface flags. Currently only - * IFF_PROMISC and IFF_ALLMULTI are used. */ - tun->if_flags = ifr.ifr_flags; - DBG(KERN_INFO "%s: interface flags 0x%lx\n", - tun->dev->name, tun->if_flags); - return 0; + case TUNSETTXFILTER: + /* Can be set only for TAPs */ + if ((tun->flags & TUN_TYPE_MASK) != TUN_TAP_DEV) + return -EINVAL; + rtnl_lock(); + ret = update_filter(&tun->txflt, (void *) __user arg); + rtnl_unlock(); + return ret; case SIOCGIFHWADDR: - /* Note: the actual net device's address may be different */ - memcpy(ifr.ifr_hwaddr.sa_data, tun->dev_addr, - min(sizeof ifr.ifr_hwaddr.sa_data, sizeof tun->dev_addr)); - if (copy_to_user( argp, &ifr, sizeof ifr)) + /* Get hw addres */ + memcpy(ifr.ifr_hwaddr.sa_data, tun->dev->dev_addr, ETH_ALEN); + ifr.ifr_hwaddr.sa_family = tun->dev->type; + if (copy_to_user(argp, &ifr, sizeof ifr)) return -EFAULT; return 0; case SIOCSIFHWADDR: - { - /* try to set the actual net device's hw address */ - int ret; + /* Set hw address */ + DBG(KERN_DEBUG "%s: set hw address: %s\n", + tun->dev->name, print_mac(mac, ifr.ifr_hwaddr.sa_data)); rtnl_lock(); ret = dev_set_mac_address(tun->dev, &ifr.ifr_hwaddr); rtnl_unlock(); - - if (ret == 0) { - /** Set the character device's hardware address. This is used when - * filtering packets being sent from the network device to the character - * device. */ - memcpy(tun->dev_addr, ifr.ifr_hwaddr.sa_data, - min(sizeof ifr.ifr_hwaddr.sa_data, sizeof tun->dev_addr)); - DBG(KERN_DEBUG "%s: set hardware address: %x:%x:%x:%x:%x:%x\n", - tun->dev->name, - tun->dev_addr[0], tun->dev_addr[1], tun->dev_addr[2], - tun->dev_addr[3], tun->dev_addr[4], tun->dev_addr[5]); - } - - return ret; - } - - case SIOCADDMULTI: - /** Add the specified group to the character device's multicast filter - * list. */ - rtnl_lock(); - netif_tx_lock_bh(tun->dev); - add_multi(tun->chr_filter, ifr.ifr_hwaddr.sa_data); - netif_tx_unlock_bh(tun->dev); - rtnl_unlock(); - - DBG(KERN_DEBUG "%s: add multi: %s\n", - tun->dev->name, print_mac(mac, ifr.ifr_hwaddr.sa_data)); - return 0; - - case SIOCDELMULTI: - /** Remove the specified group from the character device's multicast - * filter list. */ - rtnl_lock(); - netif_tx_lock_bh(tun->dev); - del_multi(tun->chr_filter, ifr.ifr_hwaddr.sa_data); - netif_tx_unlock_bh(tun->dev); - rtnl_unlock(); - - DBG(KERN_DEBUG "%s: del multi: %s\n", - tun->dev->name, print_mac(mac, ifr.ifr_hwaddr.sa_data)); - return 0; + return ret; default: return -EINVAL; diff --git a/include/linux/if_tun.h b/include/linux/if_tun.h index 563fae542da6..4c6307ad9fdb 100644 --- a/include/linux/if_tun.h +++ b/include/linux/if_tun.h @@ -17,6 +17,7 @@ #define __IF_TUN_H #include +#include /* Read queue size */ #define TUN_READQ_SIZE 500 @@ -42,7 +43,8 @@ #define TUNSETLINK _IOW('T', 205, int) #define TUNSETGROUP _IOW('T', 206, int) #define TUNGETFEATURES _IOR('T', 207, unsigned int) -#define TUNSETOFFLOAD _IOW('T', 208, unsigned int) +#define TUNSETOFFLOAD _IOW('T', 208, unsigned int) +#define TUNSETTXFILTER _IOW('T', 209, unsigned int) /* TUNSETIFF ifr flags */ #define IFF_TUN 0x0001 @@ -57,10 +59,26 @@ #define TUN_F_TSO6 0x04 /* I can handle TSO for IPv6 packets */ #define TUN_F_TSO_ECN 0x08 /* I can handle TSO with ECN bits. */ +/* Protocol info prepended to the packets (when IFF_NO_PI is not set) */ +#define TUN_PKT_STRIP 0x0001 struct tun_pi { - unsigned short flags; + __u16 flags; __be16 proto; }; -#define TUN_PKT_STRIP 0x0001 + +/* + * Filter spec (used for SETXXFILTER ioctls) + * This stuff is applicable only to the TAP (Ethernet) devices. + * If the count is zero the filter is disabled and the driver accepts + * all packets (promisc mode). + * If the filter is enabled in order to accept broadcast packets + * broadcast addr must be explicitly included in the addr list. + */ +#define TUN_FLT_ALLMULTI 0x0001 /* Accept all multicast packets */ +struct tun_filter { + __u16 flags; /* TUN_FLT_ flags see above */ + __u16 count; /* Number of addresses */ + __u8 addr[0][ETH_ALEN]; +}; #endif /* __IF_TUN_H */ -- cgit v1.2.3 From 6aa895b047720f71ec4eb11452f7c3ce8426941f Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Mon, 14 Jul 2008 22:49:06 -0700 Subject: vlan: Don't store VLAN tag in cb Use a real skb member to store the skb to avoid clashes with qdiscs, which are allowed to use the cb area themselves. As currently only real devices that consume the skb set the NETIF_F_HW_VLAN_TX flag, no explicit invalidation is neccessary. The new member fills a hole on 64 bit, the skb layout changes from: __u32 mark; /* 172 4 */ sk_buff_data_t transport_header; /* 176 4 */ sk_buff_data_t network_header; /* 180 4 */ sk_buff_data_t mac_header; /* 184 4 */ sk_buff_data_t tail; /* 188 4 */ /* --- cacheline 3 boundary (192 bytes) --- */ sk_buff_data_t end; /* 192 4 */ /* XXX 4 bytes hole, try to pack */ to __u32 mark; /* 172 4 */ __u16 vlan_tci; /* 176 2 */ /* XXX 2 bytes hole, try to pack */ sk_buff_data_t transport_header; /* 180 4 */ sk_buff_data_t network_header; /* 184 4 */ Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/if_vlan.h | 31 +++++++------------------------ include/linux/skbuff.h | 3 +++ net/core/skbuff.c | 3 +++ 3 files changed, 13 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 93f5d9b0e9f9..9e7b49b8062d 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -105,17 +105,8 @@ static inline void vlan_group_set_device(struct vlan_group *vg, array[vlan_id % VLAN_GROUP_ARRAY_PART_LEN] = dev; } -/* VLAN tx hw acceleration helpers. */ -struct vlan_skb_tx_cookie { - u32 magic; - u32 vlan_tag; -}; - -#define VLAN_TX_COOKIE_MAGIC 0x564c414e /* "VLAN" in ascii. */ -#define VLAN_TX_SKB_CB(__skb) ((struct vlan_skb_tx_cookie *)&((__skb)->cb[0])) -#define vlan_tx_tag_present(__skb) \ - (VLAN_TX_SKB_CB(__skb)->magic == VLAN_TX_COOKIE_MAGIC) -#define vlan_tx_tag_get(__skb) (VLAN_TX_SKB_CB(__skb)->vlan_tag) +#define vlan_tx_tag_present(__skb) ((__skb)->vlan_tci) +#define vlan_tx_tag_get(__skb) ((__skb)->vlan_tci) #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) extern struct net_device *vlan_dev_real_dev(const struct net_device *dev); @@ -210,17 +201,12 @@ static inline struct sk_buff *__vlan_put_tag(struct sk_buff *skb, u16 vlan_tci) * @skb: skbuff to tag * @vlan_tci: VLAN TCI to insert * - * Puts the VLAN TCI in @skb->cb[] and lets the device do the rest + * Puts the VLAN TCI in @skb->vlan_tci and lets the device do the rest */ static inline struct sk_buff *__vlan_hwaccel_put_tag(struct sk_buff *skb, u16 vlan_tci) { - struct vlan_skb_tx_cookie *cookie; - - cookie = VLAN_TX_SKB_CB(skb); - cookie->magic = VLAN_TX_COOKIE_MAGIC; - cookie->vlan_tag = vlan_tci; - + skb->vlan_tci = vlan_tci; return skb; } @@ -267,16 +253,13 @@ static inline int __vlan_get_tag(const struct sk_buff *skb, u16 *vlan_tci) * @skb: skbuff to query * @vlan_tci: buffer to store vlaue * - * Returns error if @skb->cb[] is not set correctly + * Returns error if @skb->vlan_tci is not set correctly */ static inline int __vlan_hwaccel_get_tag(const struct sk_buff *skb, u16 *vlan_tci) { - struct vlan_skb_tx_cookie *cookie; - - cookie = VLAN_TX_SKB_CB(skb); - if (cookie->magic == VLAN_TX_COOKIE_MAGIC) { - *vlan_tci = cookie->vlan_tag; + if (vlan_tx_tag_present(skb)) { + *vlan_tci = skb->vlan_tci; return 0; } else { *vlan_tci = 0; diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 8f10e3d08fd9..7ea44f6621f2 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -246,6 +246,7 @@ typedef unsigned char *sk_buff_data_t; * @dma_cookie: a cookie to one of several possible DMA operations * done by skb DMA functions * @secmark: security marking + * @vlan_tci: vlan tag control information */ struct sk_buff { @@ -326,6 +327,8 @@ struct sk_buff { __u32 mark; + __u16 vlan_tci; + sk_buff_data_t transport_header; sk_buff_data_t network_header; sk_buff_data_t mac_header; diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 7c571560e9d2..50a853f7cd8e 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -459,6 +459,8 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old) new->tc_verd = old->tc_verd; #endif #endif + new->vlan_tci = old->vlan_tci; + skb_copy_secmark(new, old); } @@ -2286,6 +2288,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int features) skb_copy_queue_mapping(nskb, skb); nskb->priority = skb->priority; nskb->protocol = skb->protocol; + nskb->vlan_tci = skb->vlan_tci; nskb->dst = dst_clone(skb->dst); memcpy(nskb->cb, skb->cb, sizeof(skb->cb)); nskb->pkt_type = skb->pkt_type; -- cgit v1.2.3 From bc1d0411b804ad190cdadabac48a10067f17b9e6 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Mon, 14 Jul 2008 22:49:30 -0700 Subject: vlan: deliver packets received with VLAN acceleration to network taps When VLAN header stripping is used, packets currently bypass packet sockets (and other network taps) completely. For locally existing VLANs, they appear directly on the VLAN device, for unknown VLANs they are silently dropped. Add a new function netif_nit_deliver() to deliver incoming packets to all network interface taps and use it in __vlan_hwaccel_rx() to make VLAN packets visible on the underlying device. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 + net/8021q/vlan_core.c | 4 ++++ net/core/dev.c | 27 +++++++++++++++++++++++++++ 3 files changed, 32 insertions(+) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index b54ec16dfbda..ba5c4639ea91 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1165,6 +1165,7 @@ extern int netif_rx(struct sk_buff *skb); extern int netif_rx_ni(struct sk_buff *skb); #define HAVE_NETIF_RECEIVE_SKB 1 extern int netif_receive_skb(struct sk_buff *skb); +extern void netif_nit_deliver(struct sk_buff *skb); extern int dev_valid_name(const char *name); extern int dev_ioctl(struct net *net, unsigned int cmd, void __user *); extern int dev_ethtool(struct net *net, struct ifreq *); diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c index 68df12d3664b..916061f681b6 100644 --- a/net/8021q/vlan_core.c +++ b/net/8021q/vlan_core.c @@ -14,6 +14,9 @@ int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, return NET_RX_DROP; } + skb->vlan_tci = vlan_tci; + netif_nit_deliver(skb); + skb->dev = vlan_group_get_device(grp, vlan_tci & VLAN_VID_MASK); if (skb->dev == NULL) { dev_kfree_skb_any(skb); @@ -22,6 +25,7 @@ int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp, return NET_RX_SUCCESS; } skb->dev->last_rx = jiffies; + skb->vlan_tci = 0; stats = &skb->dev->stats; stats->rx_packets++; diff --git a/net/core/dev.c b/net/core/dev.c index a29a359b15d1..feaab4898a5b 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2068,6 +2068,33 @@ out: } #endif +/* + * netif_nit_deliver - deliver received packets to network taps + * @skb: buffer + * + * This function is used to deliver incoming packets to network + * taps. It should be used when the normal netif_receive_skb path + * is bypassed, for example because of VLAN acceleration. + */ +void netif_nit_deliver(struct sk_buff *skb) +{ + struct packet_type *ptype; + + if (list_empty(&ptype_all)) + return; + + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + skb->mac_len = skb->network_header - skb->mac_header; + + rcu_read_lock(); + list_for_each_entry_rcu(ptype, &ptype_all, list) { + if (!ptype->dev || ptype->dev == skb->dev) + deliver_skb(skb, ptype, skb->dev); + } + rcu_read_unlock(); +} + /** * netif_receive_skb - process receive buffer from network * @skb: buffer to process -- cgit v1.2.3 From bbd6ef87c544d88c30e4b762b1b61ef267a7d279 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Mon, 14 Jul 2008 22:50:15 -0700 Subject: packet: support extensible, 64 bit clean mmaped ring structure The tpacket_hdr is not 64 bit clean due to use of an unsigned long and can't be extended because the following struct sockaddr_ll needs to be at a fixed offset. Add support for a version 2 tpacket protocol that removes these limitations. Userspace can query the header size through a new getsockopt option and change the protocol version through a setsockopt option. The changes needed to switch to the new protocol version are: 1. replace struct tpacket_hdr by struct tpacket2_hdr 2. query header len and save 3. set protocol version to 2 - set up ring as usual 4. for getting the sockaddr_ll, use (void *)hdr + TPACKET_ALIGN(hdrlen) instead of (void *)hdr + TPACKET_ALIGN(sizeof(struct tpacket_hdr)) Steps 2 and 4 can be omitted if the struct sockaddr_ll isn't needed. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/if_packet.h | 21 ++++++ net/packet/af_packet.c | 179 +++++++++++++++++++++++++++++++++++++--------- 2 files changed, 167 insertions(+), 33 deletions(-) (limited to 'include/linux') diff --git a/include/linux/if_packet.h b/include/linux/if_packet.h index ad09609227ff..d4d3c82448f5 100644 --- a/include/linux/if_packet.h +++ b/include/linux/if_packet.h @@ -43,6 +43,8 @@ struct sockaddr_ll #define PACKET_COPY_THRESH 7 #define PACKET_AUXDATA 8 #define PACKET_ORIGDEV 9 +#define PACKET_VERSION 10 +#define PACKET_HDRLEN 11 struct tpacket_stats { @@ -79,6 +81,25 @@ struct tpacket_hdr #define TPACKET_ALIGN(x) (((x)+TPACKET_ALIGNMENT-1)&~(TPACKET_ALIGNMENT-1)) #define TPACKET_HDRLEN (TPACKET_ALIGN(sizeof(struct tpacket_hdr)) + sizeof(struct sockaddr_ll)) +struct tpacket2_hdr +{ + __u32 tp_status; + __u32 tp_len; + __u32 tp_snaplen; + __u16 tp_mac; + __u16 tp_net; + __u32 tp_sec; + __u32 tp_nsec; +}; + +#define TPACKET2_HDRLEN (TPACKET_ALIGN(sizeof(struct tpacket2_hdr)) + sizeof(struct sockaddr_ll)) + +enum tpacket_versions +{ + TPACKET_V1, + TPACKET_V2, +}; + /* Frame structure: diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 9f2269166687..4f059775d48f 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -186,6 +186,8 @@ struct packet_sock { unsigned int pg_vec_order; unsigned int pg_vec_pages; unsigned int pg_vec_len; + enum tpacket_versions tp_version; + unsigned int tp_hdrlen; #endif }; @@ -201,14 +203,52 @@ struct packet_skb_cb { #ifdef CONFIG_PACKET_MMAP -static inline struct tpacket_hdr *packet_lookup_frame(struct packet_sock *po, unsigned int position) +static void *packet_lookup_frame(struct packet_sock *po, unsigned int position, + int status) { unsigned int pg_vec_pos, frame_offset; + union { + struct tpacket_hdr *h1; + struct tpacket2_hdr *h2; + void *raw; + } h; pg_vec_pos = position / po->frames_per_block; frame_offset = position % po->frames_per_block; - return (struct tpacket_hdr *)(po->pg_vec[pg_vec_pos] + (frame_offset * po->frame_size)); + h.raw = po->pg_vec[pg_vec_pos] + (frame_offset * po->frame_size); + switch (po->tp_version) { + case TPACKET_V1: + if (status != h.h1->tp_status ? TP_STATUS_USER : + TP_STATUS_KERNEL) + return NULL; + break; + case TPACKET_V2: + if (status != h.h2->tp_status ? TP_STATUS_USER : + TP_STATUS_KERNEL) + return NULL; + break; + } + return h.raw; +} + +static void __packet_set_status(struct packet_sock *po, void *frame, int status) +{ + union { + struct tpacket_hdr *h1; + struct tpacket2_hdr *h2; + void *raw; + } h; + + h.raw = frame; + switch (po->tp_version) { + case TPACKET_V1: + h.h1->tp_status = status; + break; + case TPACKET_V2: + h.h2->tp_status = status; + break; + } } #endif @@ -551,14 +591,19 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, struct packe struct sock *sk; struct packet_sock *po; struct sockaddr_ll *sll; - struct tpacket_hdr *h; + union { + struct tpacket_hdr *h1; + struct tpacket2_hdr *h2; + void *raw; + } h; u8 * skb_head = skb->data; int skb_len = skb->len; unsigned int snaplen, res; unsigned long status = TP_STATUS_LOSING|TP_STATUS_USER; - unsigned short macoff, netoff; + unsigned short macoff, netoff, hdrlen; struct sk_buff *copy_skb = NULL; struct timeval tv; + struct timespec ts; if (skb->pkt_type == PACKET_LOOPBACK) goto drop; @@ -590,10 +635,11 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, struct packe snaplen = res; if (sk->sk_type == SOCK_DGRAM) { - macoff = netoff = TPACKET_ALIGN(TPACKET_HDRLEN) + 16; + macoff = netoff = TPACKET_ALIGN(po->tp_hdrlen) + 16; } else { unsigned maclen = skb_network_offset(skb); - netoff = TPACKET_ALIGN(TPACKET_HDRLEN + (maclen < 16 ? 16 : maclen)); + netoff = TPACKET_ALIGN(po->tp_hdrlen + + (maclen < 16 ? 16 : maclen)); macoff = netoff - maclen; } @@ -616,9 +662,8 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, struct packe } spin_lock(&sk->sk_receive_queue.lock); - h = packet_lookup_frame(po, po->head); - - if (h->tp_status) + h.raw = packet_lookup_frame(po, po->head, TP_STATUS_KERNEL); + if (!h.raw) goto ring_is_full; po->head = po->head != po->frame_max ? po->head+1 : 0; po->stats.tp_packets++; @@ -630,20 +675,40 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, struct packe status &= ~TP_STATUS_LOSING; spin_unlock(&sk->sk_receive_queue.lock); - skb_copy_bits(skb, 0, (u8*)h + macoff, snaplen); + skb_copy_bits(skb, 0, h.raw + macoff, snaplen); - h->tp_len = skb->len; - h->tp_snaplen = snaplen; - h->tp_mac = macoff; - h->tp_net = netoff; - if (skb->tstamp.tv64) - tv = ktime_to_timeval(skb->tstamp); - else - do_gettimeofday(&tv); - h->tp_sec = tv.tv_sec; - h->tp_usec = tv.tv_usec; + switch (po->tp_version) { + case TPACKET_V1: + h.h1->tp_len = skb->len; + h.h1->tp_snaplen = snaplen; + h.h1->tp_mac = macoff; + h.h1->tp_net = netoff; + if (skb->tstamp.tv64) + tv = ktime_to_timeval(skb->tstamp); + else + do_gettimeofday(&tv); + h.h1->tp_sec = tv.tv_sec; + h.h1->tp_usec = tv.tv_usec; + hdrlen = sizeof(*h.h1); + break; + case TPACKET_V2: + h.h2->tp_len = skb->len; + h.h2->tp_snaplen = snaplen; + h.h2->tp_mac = macoff; + h.h2->tp_net = netoff; + if (skb->tstamp.tv64) + ts = ktime_to_timespec(skb->tstamp); + else + getnstimeofday(&ts); + h.h2->tp_sec = ts.tv_sec; + h.h2->tp_nsec = ts.tv_nsec; + hdrlen = sizeof(*h.h2); + break; + default: + BUG(); + } - sll = (struct sockaddr_ll*)((u8*)h + TPACKET_ALIGN(sizeof(*h))); + sll = h.raw + TPACKET_ALIGN(hdrlen); sll->sll_halen = dev_parse_header(skb, sll->sll_addr); sll->sll_family = AF_PACKET; sll->sll_hatype = dev->type; @@ -654,14 +719,14 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, struct packe else sll->sll_ifindex = dev->ifindex; - h->tp_status = status; + __packet_set_status(po, h.raw, status); smp_mb(); { struct page *p_start, *p_end; - u8 *h_end = (u8 *)h + macoff + snaplen - 1; + u8 *h_end = h.raw + macoff + snaplen - 1; - p_start = virt_to_page(h); + p_start = virt_to_page(h.raw); p_end = virt_to_page(h_end); while (p_start <= p_end) { flush_dcache_page(p_start); @@ -1362,6 +1427,25 @@ packet_setsockopt(struct socket *sock, int level, int optname, char __user *optv pkt_sk(sk)->copy_thresh = val; return 0; } + case PACKET_VERSION: + { + int val; + + if (optlen != sizeof(val)) + return -EINVAL; + if (po->pg_vec) + return -EBUSY; + if (copy_from_user(&val, optval, sizeof(val))) + return -EFAULT; + switch (val) { + case TPACKET_V1: + case TPACKET_V2: + po->tp_version = val; + return 0; + default: + return -EINVAL; + } + } #endif case PACKET_AUXDATA: { @@ -1437,6 +1521,31 @@ static int packet_getsockopt(struct socket *sock, int level, int optname, data = &val; break; +#ifdef CONFIG_PACKET_MMAP + case PACKET_VERSION: + if (len > sizeof(int)) + len = sizeof(int); + val = po->tp_version; + data = &val; + break; + case PACKET_HDRLEN: + if (len > sizeof(int)) + len = sizeof(int); + if (copy_from_user(&val, optval, len)) + return -EFAULT; + switch (val) { + case TPACKET_V1: + val = sizeof(struct tpacket_hdr); + break; + case TPACKET_V2: + val = sizeof(struct tpacket2_hdr); + break; + default: + return -EINVAL; + } + data = &val; + break; +#endif default: return -ENOPROTOOPT; } @@ -1570,11 +1679,8 @@ static unsigned int packet_poll(struct file * file, struct socket *sock, spin_lock_bh(&sk->sk_receive_queue.lock); if (po->pg_vec) { unsigned last = po->head ? po->head-1 : po->frame_max; - struct tpacket_hdr *h; - - h = packet_lookup_frame(po, last); - if (h->tp_status) + if (packet_lookup_frame(po, last, TP_STATUS_USER)) mask |= POLLIN | POLLRDNORM; } spin_unlock_bh(&sk->sk_receive_queue.lock); @@ -1669,11 +1775,20 @@ static int packet_set_ring(struct sock *sk, struct tpacket_req *req, int closing if (unlikely(po->pg_vec)) return -EBUSY; + switch (po->tp_version) { + case TPACKET_V1: + po->tp_hdrlen = TPACKET_HDRLEN; + break; + case TPACKET_V2: + po->tp_hdrlen = TPACKET2_HDRLEN; + break; + } + if (unlikely((int)req->tp_block_size <= 0)) return -EINVAL; if (unlikely(req->tp_block_size & (PAGE_SIZE - 1))) return -EINVAL; - if (unlikely(req->tp_frame_size < TPACKET_HDRLEN)) + if (unlikely(req->tp_frame_size < po->tp_hdrlen)) return -EINVAL; if (unlikely(req->tp_frame_size & (TPACKET_ALIGNMENT - 1))) return -EINVAL; @@ -1692,13 +1807,11 @@ static int packet_set_ring(struct sock *sk, struct tpacket_req *req, int closing goto out; for (i = 0; i < req->tp_block_nr; i++) { - char *ptr = pg_vec[i]; - struct tpacket_hdr *header; + void *ptr = pg_vec[i]; int k; for (k = 0; k < po->frames_per_block; k++) { - header = (struct tpacket_hdr *) ptr; - header->tp_status = TP_STATUS_KERNEL; + __packet_set_status(po, ptr, TP_STATUS_KERNEL); ptr += req->tp_frame_size; } } -- cgit v1.2.3 From 393e52e33c6c26ec7db290dab803bac1bed962d4 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Mon, 14 Jul 2008 22:50:39 -0700 Subject: packet: deliver VLAN TCI to userspace Store the VLAN tag in the auxillary data/tpacket2_hdr so userspace can properly deal with hardware VLAN tagging/stripping. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/if_packet.h | 2 ++ net/packet/af_packet.c | 2 ++ 2 files changed, 4 insertions(+) (limited to 'include/linux') diff --git a/include/linux/if_packet.h b/include/linux/if_packet.h index d4d3c82448f5..a630295b255f 100644 --- a/include/linux/if_packet.h +++ b/include/linux/if_packet.h @@ -59,6 +59,7 @@ struct tpacket_auxdata __u32 tp_snaplen; __u16 tp_mac; __u16 tp_net; + __u16 tp_vlan_tci; }; struct tpacket_hdr @@ -90,6 +91,7 @@ struct tpacket2_hdr __u16 tp_net; __u32 tp_sec; __u32 tp_nsec; + __u16 tp_vlan_tci; }; #define TPACKET2_HDRLEN (TPACKET_ALIGN(sizeof(struct tpacket2_hdr)) + sizeof(struct sockaddr_ll)) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 4f059775d48f..db792e02a37f 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -702,6 +702,7 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, struct packe getnstimeofday(&ts); h.h2->tp_sec = ts.tv_sec; h.h2->tp_nsec = ts.tv_nsec; + h.h2->tp_vlan_tci = skb->vlan_tci; hdrlen = sizeof(*h.h2); break; default: @@ -1172,6 +1173,7 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock, aux.tp_snaplen = skb->len; aux.tp_mac = 0; aux.tp_net = skb_network_offset(skb); + aux.tp_vlan_tci = skb->vlan_tci; put_cmsg(msg, SOL_PACKET, PACKET_AUXDATA, sizeof(aux), &aux); } -- cgit v1.2.3 From f1f28aa3510ddb84c966bac65611bb866c77a092 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 15 Jul 2008 00:08:33 -0700 Subject: netdev: Add addr_list_lock to struct net_device. This will be used to protect the per-device unicast and multicast address lists, as well as the callbacks into the drivers which configure such state such as ->set_rx_mode() and ->set_multicast_list(). Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 + net/core/dev.c | 1 + 2 files changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index ba5c4639ea91..fd0365219181 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -609,6 +609,7 @@ struct net_device unsigned char addr_len; /* hardware address length */ unsigned short dev_id; /* for shared network cards */ + spinlock_t addr_list_lock; struct dev_addr_list *uc_list; /* Secondary unicast mac addresses */ int uc_count; /* Number of installed ucasts */ int uc_promisc; diff --git a/net/core/dev.c b/net/core/dev.c index feaab4898a5b..d933d1bfa6fa 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3836,6 +3836,7 @@ int register_netdevice(struct net_device *dev) BUG_ON(!dev_net(dev)); net = dev_net(dev); + spin_lock_init(&dev->addr_list_lock); netdev_init_queue_locks(dev); dev->iflink = -1; -- cgit v1.2.3 From e308a5d806c852f56590ffdd3834d0df0cbed8d7 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 15 Jul 2008 00:13:44 -0700 Subject: netdev: Add netdev->addr_list_lock protection. Add netif_addr_{lock,unlock}{,_bh}() helpers. Use them to protect operations that operate on or read the network device unicast and multicast address lists. Also use them in cases where the code simply wants to block calls into the driver's ->set_rx_mode() and ->set_multicast_list() methods. Signed-off-by: David S. Miller --- drivers/infiniband/ulp/ipoib/ipoib_multicast.c | 2 ++ drivers/media/dvb/dvb-core/dvb_net.c | 2 ++ drivers/net/bonding/bond_main.c | 8 ++++++++ drivers/net/forcedeth.c | 16 ++++++++++++++++ drivers/net/hamradio/6pack.c | 2 ++ drivers/net/hamradio/mkiss.c | 2 ++ drivers/net/ibm_newemac/core.c | 4 ++++ drivers/net/sfc/efx.c | 2 ++ drivers/net/wireless/libertas/main.c | 2 ++ include/linux/netdevice.h | 20 ++++++++++++++++++++ net/core/dev.c | 14 ++++++++++++++ net/core/dev_mcast.c | 12 ++++++++++++ net/mac80211/main.c | 4 ++++ net/mac80211/mlme.c | 4 ++++ 14 files changed, 94 insertions(+) (limited to 'include/linux') diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c index 3f663fb852c1..261ab7150431 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c @@ -775,6 +775,7 @@ void ipoib_mcast_restart_task(struct work_struct *work) local_irq_save(flags); netif_tx_lock(dev); + netif_addr_lock(dev); spin_lock(&priv->lock); /* @@ -851,6 +852,7 @@ void ipoib_mcast_restart_task(struct work_struct *work) } spin_unlock(&priv->lock); + netif_addr_unlock(dev); netif_tx_unlock(dev); local_irq_restore(flags); diff --git a/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb/dvb-core/dvb_net.c index c2334aef4143..809d18c663bc 100644 --- a/drivers/media/dvb/dvb-core/dvb_net.c +++ b/drivers/media/dvb/dvb-core/dvb_net.c @@ -1134,6 +1134,7 @@ static void wq_set_multicast_list (struct work_struct *work) dvb_net_feed_stop(dev); priv->rx_mode = RX_MODE_UNI; netif_tx_lock_bh(dev); + netif_addr_lock(dev); if (dev->flags & IFF_PROMISC) { dprintk("%s: promiscuous mode\n", dev->name); @@ -1158,6 +1159,7 @@ static void wq_set_multicast_list (struct work_struct *work) } } + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); dvb_net_feed_start(dev); } diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 8ae7ff313218..ea71abd6f728 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1568,10 +1568,12 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) } netif_tx_lock_bh(bond_dev); + netif_addr_lock(bond_dev); /* upload master's mc_list to new slave */ for (dmi = bond_dev->mc_list; dmi; dmi = dmi->next) { dev_mc_add (slave_dev, dmi->dmi_addr, dmi->dmi_addrlen, 0); } + netif_addr_unlock(bond_dev); netif_tx_unlock_bh(bond_dev); } @@ -1937,7 +1939,9 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev) /* flush master's mc_list from slave */ netif_tx_lock_bh(bond_dev); + netif_addr_lock(bond_dev); bond_mc_list_flush(bond_dev, slave_dev); + netif_addr_unlock(bond_dev); netif_tx_unlock_bh(bond_dev); } @@ -2060,7 +2064,9 @@ static int bond_release_all(struct net_device *bond_dev) /* flush master's mc_list from slave */ netif_tx_lock_bh(bond_dev); + netif_addr_lock(bond_dev); bond_mc_list_flush(bond_dev, slave_dev); + netif_addr_unlock(bond_dev); netif_tx_unlock_bh(bond_dev); } @@ -4674,7 +4680,9 @@ static void bond_free_all(void) bond_work_cancel_all(bond); netif_tx_lock_bh(bond_dev); + netif_addr_lock(bond_dev); bond_mc_list_destroy(bond); + netif_addr_unlock(bond_dev); netif_tx_unlock_bh(bond_dev); /* Release the bonded slaves */ bond_release_all(bond_dev); diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c index 786d668c612e..4ed89fa9ae46 100644 --- a/drivers/net/forcedeth.c +++ b/drivers/net/forcedeth.c @@ -2831,6 +2831,7 @@ static int nv_change_mtu(struct net_device *dev, int new_mtu) */ nv_disable_irq(dev); netif_tx_lock_bh(dev); + netif_addr_lock(dev); spin_lock(&np->lock); /* stop engines */ nv_stop_rxtx(dev); @@ -2855,6 +2856,7 @@ static int nv_change_mtu(struct net_device *dev, int new_mtu) /* restart rx engine */ nv_start_rxtx(dev); spin_unlock(&np->lock); + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); nv_enable_irq(dev); } @@ -2891,6 +2893,7 @@ static int nv_set_mac_address(struct net_device *dev, void *addr) if (netif_running(dev)) { netif_tx_lock_bh(dev); + netif_addr_lock(dev); spin_lock_irq(&np->lock); /* stop rx engine */ @@ -2902,6 +2905,7 @@ static int nv_set_mac_address(struct net_device *dev, void *addr) /* restart rx engine */ nv_start_rx(dev); spin_unlock_irq(&np->lock); + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); } else { nv_copy_mac_to_hw(dev); @@ -3971,6 +3975,7 @@ static void nv_do_nic_poll(unsigned long data) printk(KERN_INFO "forcedeth: MAC in recoverable error state\n"); if (netif_running(dev)) { netif_tx_lock_bh(dev); + netif_addr_lock(dev); spin_lock(&np->lock); /* stop engines */ nv_stop_rxtx(dev); @@ -3995,6 +4000,7 @@ static void nv_do_nic_poll(unsigned long data) /* restart rx engine */ nv_start_rxtx(dev); spin_unlock(&np->lock); + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); } } @@ -4202,6 +4208,7 @@ static int nv_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) nv_disable_irq(dev); netif_tx_lock_bh(dev); + netif_addr_lock(dev); /* with plain spinlock lockdep complains */ spin_lock_irqsave(&np->lock, flags); /* stop engines */ @@ -4215,6 +4222,7 @@ static int nv_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) */ nv_stop_rxtx(dev); spin_unlock_irqrestore(&np->lock, flags); + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); } @@ -4360,10 +4368,12 @@ static int nv_nway_reset(struct net_device *dev) if (netif_running(dev)) { nv_disable_irq(dev); netif_tx_lock_bh(dev); + netif_addr_lock(dev); spin_lock(&np->lock); /* stop engines */ nv_stop_rxtx(dev); spin_unlock(&np->lock); + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); printk(KERN_INFO "%s: link down.\n", dev->name); } @@ -4471,6 +4481,7 @@ static int nv_set_ringparam(struct net_device *dev, struct ethtool_ringparam* ri if (netif_running(dev)) { nv_disable_irq(dev); netif_tx_lock_bh(dev); + netif_addr_lock(dev); spin_lock(&np->lock); /* stop engines */ nv_stop_rxtx(dev); @@ -4519,6 +4530,7 @@ static int nv_set_ringparam(struct net_device *dev, struct ethtool_ringparam* ri /* restart engines */ nv_start_rxtx(dev); spin_unlock(&np->lock); + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); nv_enable_irq(dev); } @@ -4556,10 +4568,12 @@ static int nv_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam* if (netif_running(dev)) { nv_disable_irq(dev); netif_tx_lock_bh(dev); + netif_addr_lock(dev); spin_lock(&np->lock); /* stop engines */ nv_stop_rxtx(dev); spin_unlock(&np->lock); + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); } @@ -4946,6 +4960,7 @@ static void nv_self_test(struct net_device *dev, struct ethtool_test *test, u64 napi_disable(&np->napi); #endif netif_tx_lock_bh(dev); + netif_addr_lock(dev); spin_lock_irq(&np->lock); nv_disable_hw_interrupts(dev, np->irqmask); if (!(np->msi_flags & NV_MSI_X_ENABLED)) { @@ -4959,6 +4974,7 @@ static void nv_self_test(struct net_device *dev, struct ethtool_test *test, u64 /* drain rx queue */ nv_drain_rxtx(dev); spin_unlock_irq(&np->lock); + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); } diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c index 06ad9f302b5a..ffc937f5d15d 100644 --- a/drivers/net/hamradio/6pack.c +++ b/drivers/net/hamradio/6pack.c @@ -300,7 +300,9 @@ static int sp_set_mac_address(struct net_device *dev, void *addr) struct sockaddr_ax25 *sa = addr; netif_tx_lock_bh(dev); + netif_addr_lock(dev); memcpy(dev->dev_addr, &sa->sax25_call, AX25_ADDR_LEN); + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); return 0; diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c index 65166035aca0..b8740e6a5cec 100644 --- a/drivers/net/hamradio/mkiss.c +++ b/drivers/net/hamradio/mkiss.c @@ -356,7 +356,9 @@ static int ax_set_mac_address(struct net_device *dev, void *addr) struct sockaddr_ax25 *sa = addr; netif_tx_lock_bh(dev); + netif_addr_lock(dev); memcpy(dev->dev_addr, &sa->sax25_call, AX25_ADDR_LEN); + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); return 0; diff --git a/drivers/net/ibm_newemac/core.c b/drivers/net/ibm_newemac/core.c index babc79ad490b..9ca57d365599 100644 --- a/drivers/net/ibm_newemac/core.c +++ b/drivers/net/ibm_newemac/core.c @@ -295,7 +295,9 @@ static void emac_rx_disable(struct emac_instance *dev) static inline void emac_netif_stop(struct emac_instance *dev) { netif_tx_lock_bh(dev->ndev); + netif_addr_lock(dev->ndev); dev->no_mcast = 1; + netif_addr_unlock(dev->ndev); netif_tx_unlock_bh(dev->ndev); dev->ndev->trans_start = jiffies; /* prevent tx timeout */ mal_poll_disable(dev->mal, &dev->commac); @@ -305,9 +307,11 @@ static inline void emac_netif_stop(struct emac_instance *dev) static inline void emac_netif_start(struct emac_instance *dev) { netif_tx_lock_bh(dev->ndev); + netif_addr_lock(dev->ndev); dev->no_mcast = 0; if (dev->mcast_pending && netif_running(dev->ndev)) __emac_set_multicast_list(dev); + netif_addr_unlock(dev->ndev); netif_tx_unlock_bh(dev->ndev); netif_wake_queue(dev->ndev); diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c index 74265d8553b8..e1257e556e48 100644 --- a/drivers/net/sfc/efx.c +++ b/drivers/net/sfc/efx.c @@ -697,6 +697,8 @@ static void efx_stop_port(struct efx_nic *efx) /* Serialise against efx_set_multicast_list() */ if (efx_dev_registered(efx)) { netif_tx_lock_bh(efx->net_dev); + netif_addr_lock(efx->net_dev); + netif_addr_unlock(efx->net_dev); netif_tx_unlock_bh(efx->net_dev); } } diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c index abd6d9ed8f4b..42e9b2771eab 100644 --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c @@ -594,6 +594,7 @@ static int lbs_add_mcast_addrs(struct cmd_ds_mac_multicast_adr *cmd, return nr_addrs; netif_tx_lock_bh(dev); + netif_addr_lock(dev); for (mc_list = dev->mc_list; mc_list; mc_list = mc_list->next) { if (mac_in_list(cmd->maclist, nr_addrs, mc_list->dmi_addr)) { lbs_deb_net("mcast address %s:%s skipped\n", dev->name, @@ -608,6 +609,7 @@ static int lbs_add_mcast_addrs(struct cmd_ds_mac_multicast_adr *cmd, print_mac(mac, mc_list->dmi_addr)); i++; } + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); if (mc_list) return -EOVERFLOW; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index fd0365219181..570cf7affa72 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1498,6 +1498,26 @@ static inline void netif_tx_disable(struct net_device *dev) netif_tx_unlock_bh(dev); } +static inline void netif_addr_lock(struct net_device *dev) +{ + spin_lock(&dev->addr_list_lock); +} + +static inline void netif_addr_lock_bh(struct net_device *dev) +{ + spin_lock_bh(&dev->addr_list_lock); +} + +static inline void netif_addr_unlock(struct net_device *dev) +{ + spin_unlock(&dev->addr_list_lock); +} + +static inline void netif_addr_unlock_bh(struct net_device *dev) +{ + spin_unlock_bh(&dev->addr_list_lock); +} + /* These functions live elsewhere (drivers/net/net_init.c, but related) */ extern void ether_setup(struct net_device *dev); diff --git a/net/core/dev.c b/net/core/dev.c index d933d1bfa6fa..ef1502d71f25 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2982,7 +2982,9 @@ void __dev_set_rx_mode(struct net_device *dev) void dev_set_rx_mode(struct net_device *dev) { netif_tx_lock_bh(dev); + netif_addr_lock(dev); __dev_set_rx_mode(dev); + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); } @@ -3062,9 +3064,11 @@ int dev_unicast_delete(struct net_device *dev, void *addr, int alen) ASSERT_RTNL(); netif_tx_lock_bh(dev); + netif_addr_lock(dev); err = __dev_addr_delete(&dev->uc_list, &dev->uc_count, addr, alen, 0); if (!err) __dev_set_rx_mode(dev); + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); return err; } @@ -3088,9 +3092,11 @@ int dev_unicast_add(struct net_device *dev, void *addr, int alen) ASSERT_RTNL(); netif_tx_lock_bh(dev); + netif_addr_lock(dev); err = __dev_addr_add(&dev->uc_list, &dev->uc_count, addr, alen, 0); if (!err) __dev_set_rx_mode(dev); + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); return err; } @@ -3159,10 +3165,12 @@ int dev_unicast_sync(struct net_device *to, struct net_device *from) int err = 0; netif_tx_lock_bh(to); + netif_addr_lock(to); err = __dev_addr_sync(&to->uc_list, &to->uc_count, &from->uc_list, &from->uc_count); if (!err) __dev_set_rx_mode(to); + netif_addr_unlock(to); netif_tx_unlock_bh(to); return err; } @@ -3180,13 +3188,17 @@ EXPORT_SYMBOL(dev_unicast_sync); void dev_unicast_unsync(struct net_device *to, struct net_device *from) { netif_tx_lock_bh(from); + netif_addr_lock(from); netif_tx_lock_bh(to); + netif_addr_lock(to); __dev_addr_unsync(&to->uc_list, &to->uc_count, &from->uc_list, &from->uc_count); __dev_set_rx_mode(to); + netif_addr_unlock(to); netif_tx_unlock_bh(to); + netif_addr_unlock(from); netif_tx_unlock_bh(from); } EXPORT_SYMBOL(dev_unicast_unsync); @@ -3208,6 +3220,7 @@ static void __dev_addr_discard(struct dev_addr_list **list) static void dev_addr_discard(struct net_device *dev) { netif_tx_lock_bh(dev); + netif_addr_lock(dev); __dev_addr_discard(&dev->uc_list); dev->uc_count = 0; @@ -3215,6 +3228,7 @@ static void dev_addr_discard(struct net_device *dev) __dev_addr_discard(&dev->mc_list); dev->mc_count = 0; + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); } diff --git a/net/core/dev_mcast.c b/net/core/dev_mcast.c index f8a3455f4493..b6b2a129971a 100644 --- a/net/core/dev_mcast.c +++ b/net/core/dev_mcast.c @@ -73,6 +73,7 @@ int dev_mc_delete(struct net_device *dev, void *addr, int alen, int glbl) int err; netif_tx_lock_bh(dev); + netif_addr_lock(dev); err = __dev_addr_delete(&dev->mc_list, &dev->mc_count, addr, alen, glbl); if (!err) { @@ -83,6 +84,7 @@ int dev_mc_delete(struct net_device *dev, void *addr, int alen, int glbl) __dev_set_rx_mode(dev); } + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); return err; } @@ -96,9 +98,11 @@ int dev_mc_add(struct net_device *dev, void *addr, int alen, int glbl) int err; netif_tx_lock_bh(dev); + netif_addr_lock(dev); err = __dev_addr_add(&dev->mc_list, &dev->mc_count, addr, alen, glbl); if (!err) __dev_set_rx_mode(dev); + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); return err; } @@ -120,10 +124,12 @@ int dev_mc_sync(struct net_device *to, struct net_device *from) int err = 0; netif_tx_lock_bh(to); + netif_addr_lock(to); err = __dev_addr_sync(&to->mc_list, &to->mc_count, &from->mc_list, &from->mc_count); if (!err) __dev_set_rx_mode(to); + netif_addr_unlock(to); netif_tx_unlock_bh(to); return err; @@ -144,13 +150,17 @@ EXPORT_SYMBOL(dev_mc_sync); void dev_mc_unsync(struct net_device *to, struct net_device *from) { netif_tx_lock_bh(from); + netif_addr_lock(from); netif_tx_lock_bh(to); + netif_addr_lock(to); __dev_addr_unsync(&to->mc_list, &to->mc_count, &from->mc_list, &from->mc_count); __dev_set_rx_mode(to); + netif_addr_unlock(to); netif_tx_unlock_bh(to); + netif_addr_unlock(from); netif_tx_unlock_bh(from); } EXPORT_SYMBOL(dev_mc_unsync); @@ -165,6 +175,7 @@ static int dev_mc_seq_show(struct seq_file *seq, void *v) return 0; netif_tx_lock_bh(dev); + netif_addr_lock(dev); for (m = dev->mc_list; m; m = m->next) { int i; @@ -176,6 +187,7 @@ static int dev_mc_seq_show(struct seq_file *seq, void *v) seq_putc(seq, '\n'); } + netif_addr_unlock(dev); netif_tx_unlock_bh(dev); return 0; } diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 36859e794928..095b7d928d64 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -292,7 +292,9 @@ static int ieee80211_open(struct net_device *dev) local->fif_other_bss++; netif_tx_lock_bh(local->mdev); + netif_addr_lock(local->mdev); ieee80211_configure_filter(local); + netif_addr_unlock(local->mdev); netif_tx_unlock_bh(local->mdev); break; case IEEE80211_IF_TYPE_STA: @@ -491,7 +493,9 @@ static int ieee80211_stop(struct net_device *dev) local->fif_other_bss--; netif_tx_lock_bh(local->mdev); + netif_addr_lock(local->mdev); ieee80211_configure_filter(local); + netif_addr_unlock(local->mdev); netif_tx_unlock_bh(local->mdev); break; case IEEE80211_IF_TYPE_MESH_POINT: diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 8f51375317dd..1232ba25e1e9 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3869,6 +3869,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw) netif_tx_lock_bh(local->mdev); + netif_addr_lock(local->mdev); local->filter_flags &= ~FIF_BCN_PRBRESP_PROMISC; local->ops->configure_filter(local_to_hw(local), FIF_BCN_PRBRESP_PROMISC, @@ -3876,6 +3877,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw) local->mdev->mc_count, local->mdev->mc_list); + netif_addr_unlock(local->mdev); netif_tx_unlock_bh(local->mdev); rcu_read_lock(); @@ -4063,12 +4065,14 @@ static int ieee80211_sta_start_scan(struct net_device *dev, local->scan_dev = dev; netif_tx_lock_bh(local->mdev); + netif_addr_lock(local->mdev); local->filter_flags |= FIF_BCN_PRBRESP_PROMISC; local->ops->configure_filter(local_to_hw(local), FIF_BCN_PRBRESP_PROMISC, &local->filter_flags, local->mdev->mc_count, local->mdev->mc_list); + netif_addr_unlock(local->mdev); netif_tx_unlock_bh(local->mdev); /* TODO: start scan as soon as all nullfunc frames are ACKed */ -- cgit v1.2.3 From 8f920d5e29f86d3425a68e1c3bc264d1f6f55112 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 15 Jul 2008 14:06:48 -0400 Subject: lockd: eliminate duplicate nlmsvc_lookup_host call from nlmsvc_testlock nlmsvc_testlock calls nlmsvc_lookup_host to find a nlm_host struct. The callers of this functions, however, call nlmsvc_retrieve_args or nlm4svc_retrieve_args, which also return a nlm_host struct. Change nlmsvc_testlock to take a host arg instead of calling nlmsvc_lookup_host itself and change the callers to pass a pointer to the nlm_host they've already found. We take a reference to host in the place where nlmsvc_testlock() previous did a new lookup, so the reference counting is unchanged from before. Signed-off-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/lockd/svc4proc.c | 2 +- fs/lockd/svclock.c | 12 +++--------- fs/lockd/svcproc.c | 2 +- include/linux/lockd/lockd.h | 3 ++- 4 files changed, 7 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index 006a832d46f2..8cfb9daa7c77 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -99,7 +99,7 @@ nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp, return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; /* Now check for conflicting locks */ - resp->status = nlmsvc_testlock(rqstp, file, &argp->lock, &resp->lock, &resp->cookie); + resp->status = nlmsvc_testlock(rqstp, file, host, &argp->lock, &resp->lock, &resp->cookie); if (resp->status == nlm_drop_reply) rc = rpc_drop_reply; else diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 81aca859bfde..f40afb3a0e69 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -460,8 +460,8 @@ out: */ __be32 nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, - struct nlm_lock *lock, struct nlm_lock *conflock, - struct nlm_cookie *cookie) + struct nlm_host *host, struct nlm_lock *lock, + struct nlm_lock *conflock, struct nlm_cookie *cookie) { struct nlm_block *block = NULL; int error; @@ -479,16 +479,10 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file, if (block == NULL) { struct file_lock *conf = kzalloc(sizeof(*conf), GFP_KERNEL); - struct nlm_host *host; if (conf == NULL) return nlm_granted; - /* Create host handle for callback */ - host = nlmsvc_lookup_host(rqstp, lock->caller, lock->len); - if (host == NULL) { - kfree(conf); - return nlm_lck_denied_nolocks; - } + nlm_get_host(host); block = nlmsvc_create_block(rqstp, host, file, lock, cookie); if (block == NULL) { kfree(conf); diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index fce3d7039625..e099f589b61b 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -128,7 +128,7 @@ nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_args *argp, return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success; /* Now check for conflicting locks */ - resp->status = cast_status(nlmsvc_testlock(rqstp, file, &argp->lock, &resp->lock, &resp->cookie)); + resp->status = cast_status(nlmsvc_testlock(rqstp, file, host, &argp->lock, &resp->lock, &resp->cookie)); if (resp->status == nlm_drop_reply) rc = rpc_drop_reply; else diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 102d928f7206..b27967034b53 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -203,7 +203,8 @@ __be32 nlmsvc_lock(struct svc_rqst *, struct nlm_file *, struct nlm_lock *, int, struct nlm_cookie *); __be32 nlmsvc_unlock(struct nlm_file *, struct nlm_lock *); __be32 nlmsvc_testlock(struct svc_rqst *, struct nlm_file *, - struct nlm_lock *, struct nlm_lock *, struct nlm_cookie *); + struct nlm_host *, struct nlm_lock *, + struct nlm_lock *, struct nlm_cookie *); __be32 nlmsvc_cancel_blocked(struct nlm_file *, struct nlm_lock *); unsigned long nlmsvc_retry_blocked(void); void nlmsvc_traverse_blocks(struct nlm_host *, struct nlm_file *, -- cgit v1.2.3 From 6cde4de80773497d8333985b135f472eda870904 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 15 Jul 2008 14:26:17 -0400 Subject: lockd: eliminate duplicate nlmsvc_lookup_host call from nlmsvc_lock nlmsvc_lock calls nlmsvc_lookup_host to find a nlm_host struct. The callers of this function, however, call nlmsvc_retrieve_args or nlm4svc_retrieve_args, which also return a nlm_host struct. Change nlmsvc_lock to take a host arg instead of calling nlmsvc_lookup_host itself and change the callers to pass a pointer to the nlm_host they've already found. Since nlmsvc_testlock() now just uses the caller's reference, we no longer need to get or release it. Signed-off-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/lockd/svc4proc.c | 2 +- fs/lockd/svclock.c | 10 ++-------- fs/lockd/svcproc.c | 2 +- include/linux/lockd/lockd.h | 3 ++- 4 files changed, 6 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index 8cfb9daa7c77..189b2ce01da6 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -145,7 +145,7 @@ nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp, #endif /* Now try to lock the file */ - resp->status = nlmsvc_lock(rqstp, file, &argp->lock, + resp->status = nlmsvc_lock(rqstp, file, host, &argp->lock, argp->block, &argp->cookie); if (resp->status == nlm_drop_reply) rc = rpc_drop_reply; diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index f40afb3a0e69..bcf73f6e8226 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -358,10 +358,10 @@ nlmsvc_defer_lock_rqst(struct svc_rqst *rqstp, struct nlm_block *block) */ __be32 nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, - struct nlm_lock *lock, int wait, struct nlm_cookie *cookie) + struct nlm_host *host, struct nlm_lock *lock, int wait, + struct nlm_cookie *cookie) { struct nlm_block *block = NULL; - struct nlm_host *host; int error; __be32 ret; @@ -373,11 +373,6 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, (long long)lock->fl.fl_end, wait); - /* Create host handle for callback */ - host = nlmsvc_lookup_host(rqstp, lock->caller, lock->len); - if (host == NULL) - return nlm_lck_denied_nolocks; - /* Lock file against concurrent access */ mutex_lock(&file->f_mutex); /* Get existing block (in case client is busy-waiting) @@ -450,7 +445,6 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file, out: mutex_unlock(&file->f_mutex); nlmsvc_release_block(block); - nlm_release_host(host); dprintk("lockd: nlmsvc_lock returned %u\n", ret); return ret; } diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index e099f589b61b..82dc9083ba67 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -175,7 +175,7 @@ nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_args *argp, #endif /* Now try to lock the file */ - resp->status = cast_status(nlmsvc_lock(rqstp, file, &argp->lock, + resp->status = cast_status(nlmsvc_lock(rqstp, file, host, &argp->lock, argp->block, &argp->cookie)); if (resp->status == nlm_drop_reply) rc = rpc_drop_reply; diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index b27967034b53..f81f9dd5f147 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -200,7 +200,8 @@ typedef int (*nlm_host_match_fn_t)(void *cur, struct nlm_host *ref); * Server-side lock handling */ __be32 nlmsvc_lock(struct svc_rqst *, struct nlm_file *, - struct nlm_lock *, int, struct nlm_cookie *); + struct nlm_host *, struct nlm_lock *, int, + struct nlm_cookie *); __be32 nlmsvc_unlock(struct nlm_file *, struct nlm_lock *); __be32 nlmsvc_testlock(struct svc_rqst *, struct nlm_file *, struct nlm_host *, struct nlm_lock *, -- cgit v1.2.3 From 367c8c7bd9a2882daad6c9cb607e1db8ef781ad4 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 30 Jun 2008 18:58:14 -0400 Subject: lockd: Pass "struct sockaddr *" to new failover-by-IP function Pass a more generic socket address type to nlmsvc_unlock_all_by_ip() to allow for future support of IPv6. Also provide additional sanity checking in failover_unlock_ip() when constructing the server's IP address. As an added bonus, provide clean kerneldoc comments on related NLM interfaces which were recently added. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- fs/lockd/svcsubs.c | 32 +++++++++++++++++++++++--------- fs/nfsd/nfsctl.c | 15 ++++++++++----- include/linux/lockd/lockd.h | 2 +- 3 files changed, 34 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index d1c48b539df8..198b4e55b373 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -373,13 +373,16 @@ nlmsvc_free_host_resources(struct nlm_host *host) } } -/* - * Remove all locks held for clients +/** + * nlmsvc_invalidate_all - remove all locks held for clients + * + * Release all locks held by NFS clients. + * */ void nlmsvc_invalidate_all(void) { - /* Release all locks held by NFS clients. + /* * Previously, the code would call * nlmsvc_free_host_resources for each client in * turn, which is about as inefficient as it gets. @@ -396,6 +399,12 @@ nlmsvc_match_sb(void *datap, struct nlm_file *file) return sb == file->f_file->f_path.mnt->mnt_sb; } +/** + * nlmsvc_unlock_all_by_sb - release locks held on this file system + * @sb: super block + * + * Release all locks held by clients accessing this file system. + */ int nlmsvc_unlock_all_by_sb(struct super_block *sb) { @@ -409,17 +418,22 @@ EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_sb); static int nlmsvc_match_ip(void *datap, struct nlm_host *host) { - __be32 *server_addr = datap; - - return host->h_saddr.sin_addr.s_addr == *server_addr; + return nlm_cmp_addr(&host->h_saddr, datap); } +/** + * nlmsvc_unlock_all_by_ip - release local locks by IP address + * @server_addr: server's IP address as seen by clients + * + * Release all locks held by clients accessing this host + * via the passed in IP address. + */ int -nlmsvc_unlock_all_by_ip(__be32 server_addr) +nlmsvc_unlock_all_by_ip(struct sockaddr *server_addr) { int ret; - ret = nlm_traverse_files(&server_addr, nlmsvc_match_ip, NULL); - return ret ? -EIO : 0; + ret = nlm_traverse_files(server_addr, nlmsvc_match_ip, NULL); + return ret ? -EIO : 0; } EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_ip); diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 2c2eb8796c10..1955a2702e60 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -310,9 +310,12 @@ static ssize_t write_getfd(struct file *file, char *buf, size_t size) static ssize_t failover_unlock_ip(struct file *file, char *buf, size_t size) { - __be32 server_ip; - char *fo_path, c; + struct sockaddr_in sin = { + .sin_family = AF_INET, + }; int b1, b2, b3, b4; + char c; + char *fo_path; /* sanity check */ if (size == 0) @@ -326,11 +329,13 @@ static ssize_t failover_unlock_ip(struct file *file, char *buf, size_t size) return -EINVAL; /* get ipv4 address */ - if (sscanf(fo_path, "%u.%u.%u.%u%c", &b1, &b2, &b3, &b4, &c) != 4) + if (sscanf(fo_path, NIPQUAD_FMT "%c", &b1, &b2, &b3, &b4, &c) != 4) + return -EINVAL; + if (b1 > 255 || b2 > 255 || b3 > 255 || b4 > 255) return -EINVAL; - server_ip = htonl((((((b1<<8)|b2)<<8)|b3)<<8)|b4); + sin.sin_addr.s_addr = htonl((b1 << 24) | (b2 << 16) | (b3 << 8) | b4); - return nlmsvc_unlock_all_by_ip(server_ip); + return nlmsvc_unlock_all_by_ip((struct sockaddr *)&sin); } static ssize_t failover_unlock_fs(struct file *file, char *buf, size_t size) diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index f81f9dd5f147..dbb87ab282e8 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -226,7 +226,7 @@ void nlmsvc_invalidate_all(void); * Cluster failover support */ int nlmsvc_unlock_all_by_sb(struct super_block *sb); -int nlmsvc_unlock_all_by_ip(__be32 server_addr); +int nlmsvc_unlock_all_by_ip(struct sockaddr *server_addr); static inline struct inode *nlmsvc_file_inode(struct nlm_file *file) { -- cgit v1.2.3 From f470021adb9190819c03d6d8c5c860a17480aa6d Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Mon, 24 Mar 2008 18:36:23 -0700 Subject: ptrace children revamp ptrace no longer fiddles with the children/sibling links, and the old ptrace_children list is gone. Now ptrace, whether of one's own children or another's via PTRACE_ATTACH, just uses the new ptraced list instead. There should be no user-visible difference that matters. The only change is the order in which do_wait() sees multiple stopped children and stopped ptrace attachees. Since wait_task_stopped() was changed earlier so it no longer reorders the children list, we already know this won't cause any new problems. Signed-off-by: Roland McGrath --- include/linux/init_task.h | 4 +- include/linux/sched.h | 26 +++--- kernel/exit.c | 226 ++++++++++++++++++++++++---------------------- kernel/fork.c | 6 +- kernel/ptrace.c | 37 +++++--- 5 files changed, 160 insertions(+), 139 deletions(-) (limited to 'include/linux') diff --git a/include/linux/init_task.h b/include/linux/init_task.h index 9927a88674a3..93c45acf249a 100644 --- a/include/linux/init_task.h +++ b/include/linux/init_task.h @@ -140,8 +140,8 @@ extern struct group_info init_groups; .nr_cpus_allowed = NR_CPUS, \ }, \ .tasks = LIST_HEAD_INIT(tsk.tasks), \ - .ptrace_children= LIST_HEAD_INIT(tsk.ptrace_children), \ - .ptrace_list = LIST_HEAD_INIT(tsk.ptrace_list), \ + .ptraced = LIST_HEAD_INIT(tsk.ptraced), \ + .ptrace_entry = LIST_HEAD_INIT(tsk.ptrace_entry), \ .real_parent = &tsk, \ .parent = &tsk, \ .children = LIST_HEAD_INIT(tsk.children), \ diff --git a/include/linux/sched.h b/include/linux/sched.h index ba2f859c6e4f..1941d8b5cf11 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1062,12 +1062,6 @@ struct task_struct { #endif struct list_head tasks; - /* - * ptrace_list/ptrace_children forms the list of my children - * that were stolen by a ptracer. - */ - struct list_head ptrace_children; - struct list_head ptrace_list; struct mm_struct *mm, *active_mm; @@ -1089,18 +1083,25 @@ struct task_struct { /* * pointers to (original) parent process, youngest child, younger sibling, * older sibling, respectively. (p->father can be replaced with - * p->parent->pid) + * p->real_parent->pid) */ - struct task_struct *real_parent; /* real parent process (when being debugged) */ - struct task_struct *parent; /* parent process */ + struct task_struct *real_parent; /* real parent process */ + struct task_struct *parent; /* recipient of SIGCHLD, wait4() reports */ /* - * children/sibling forms the list of my children plus the - * tasks I'm ptracing. + * children/sibling forms the list of my natural children */ struct list_head children; /* list of my children */ struct list_head sibling; /* linkage in my parent's children list */ struct task_struct *group_leader; /* threadgroup leader */ + /* + * ptraced is the list of tasks this task is using ptrace on. + * This includes both natural children and PTRACE_ATTACH targets. + * p->ptrace_entry is p's link on the p->parent->ptraced list. + */ + struct list_head ptraced; + struct list_head ptrace_entry; + /* PID/PID hash table linkage. */ struct pid_link pids[PIDTYPE_MAX]; struct list_head thread_group; @@ -1876,9 +1877,6 @@ extern void wait_task_inactive(struct task_struct * p); #define wait_task_inactive(p) do { } while (0) #endif -#define remove_parent(p) list_del_init(&(p)->sibling) -#define add_parent(p) list_add_tail(&(p)->sibling,&(p)->parent->children) - #define next_task(p) list_entry(rcu_dereference((p)->tasks.next), struct task_struct, tasks) #define for_each_process(p) \ diff --git a/kernel/exit.c b/kernel/exit.c index 7453356a961f..1e909826a804 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -71,7 +71,7 @@ static void __unhash_process(struct task_struct *p) __get_cpu_var(process_counts)--; } list_del_rcu(&p->thread_group); - remove_parent(p); + list_del_init(&p->sibling); } /* @@ -152,6 +152,18 @@ static void delayed_put_task_struct(struct rcu_head *rhp) put_task_struct(container_of(rhp, struct task_struct, rcu)); } +/* + * Do final ptrace-related cleanup of a zombie being reaped. + * + * Called with write_lock(&tasklist_lock) held. + */ +static void ptrace_release_task(struct task_struct *p) +{ + BUG_ON(!list_empty(&p->ptraced)); + ptrace_unlink(p); + BUG_ON(!list_empty(&p->ptrace_entry)); +} + void release_task(struct task_struct * p) { struct task_struct *leader; @@ -160,8 +172,7 @@ repeat: atomic_dec(&p->user->processes); proc_flush_task(p); write_lock_irq(&tasklist_lock); - ptrace_unlink(p); - BUG_ON(!list_empty(&p->ptrace_list) || !list_empty(&p->ptrace_children)); + ptrace_release_task(p); __exit_signal(p); /* @@ -315,9 +326,8 @@ static void reparent_to_kthreadd(void) ptrace_unlink(current); /* Reparent to init */ - remove_parent(current); current->real_parent = current->parent = kthreadd_task; - add_parent(current); + list_move_tail(¤t->sibling, ¤t->real_parent->children); /* Set the exit signal to SIGCHLD so we signal init on exit */ current->exit_signal = SIGCHLD; @@ -692,37 +702,71 @@ static void exit_mm(struct task_struct * tsk) mmput(mm); } -static void -reparent_thread(struct task_struct *p, struct task_struct *father, int traced) +/* + * Detach all tasks we were using ptrace on. + * Any that need to be release_task'd are put on the @dead list. + * + * Called with write_lock(&tasklist_lock) held. + */ +static void ptrace_exit(struct task_struct *parent, struct list_head *dead) { - if (p->pdeath_signal) - /* We already hold the tasklist_lock here. */ - group_send_sig_info(p->pdeath_signal, SEND_SIG_NOINFO, p); + struct task_struct *p, *n; - /* Move the child from its dying parent to the new one. */ - if (unlikely(traced)) { - /* Preserve ptrace links if someone else is tracing this child. */ - list_del_init(&p->ptrace_list); - if (ptrace_reparented(p)) - list_add(&p->ptrace_list, &p->real_parent->ptrace_children); - } else { - /* If this child is being traced, then we're the one tracing it - * anyway, so let go of it. + list_for_each_entry_safe(p, n, &parent->ptraced, ptrace_entry) { + __ptrace_unlink(p); + + if (p->exit_state != EXIT_ZOMBIE) + continue; + + /* + * If it's a zombie, our attachedness prevented normal + * parent notification or self-reaping. Do notification + * now if it would have happened earlier. If it should + * reap itself, add it to the @dead list. We can't call + * release_task() here because we already hold tasklist_lock. + * + * If it's our own child, there is no notification to do. */ - p->ptrace = 0; - remove_parent(p); - p->parent = p->real_parent; - add_parent(p); + if (!task_detached(p) && thread_group_empty(p)) { + if (!same_thread_group(p->real_parent, parent)) + do_notify_parent(p, p->exit_signal); + } - if (task_is_traced(p)) { + if (task_detached(p)) { /* - * If it was at a trace stop, turn it into - * a normal stop since it's no longer being - * traced. + * Mark it as in the process of being reaped. */ - ptrace_untrace(p); + p->exit_state = EXIT_DEAD; + list_add(&p->ptrace_entry, dead); } } +} + +/* + * Finish up exit-time ptrace cleanup. + * + * Called without locks. + */ +static void ptrace_exit_finish(struct task_struct *parent, + struct list_head *dead) +{ + struct task_struct *p, *n; + + BUG_ON(!list_empty(&parent->ptraced)); + + list_for_each_entry_safe(p, n, dead, ptrace_entry) { + list_del_init(&p->ptrace_entry); + release_task(p); + } +} + +static void reparent_thread(struct task_struct *p, struct task_struct *father) +{ + if (p->pdeath_signal) + /* We already hold the tasklist_lock here. */ + group_send_sig_info(p->pdeath_signal, SEND_SIG_NOINFO, p); + + list_move_tail(&p->sibling, &p->real_parent->children); /* If this is a threaded reparent there is no need to * notify anyone anything has happened. @@ -737,7 +781,8 @@ reparent_thread(struct task_struct *p, struct task_struct *father, int traced) /* If we'd notified the old parent about this child's death, * also notify the new parent. */ - if (!traced && p->exit_state == EXIT_ZOMBIE && + if (!ptrace_reparented(p) && + p->exit_state == EXIT_ZOMBIE && !task_detached(p) && thread_group_empty(p)) do_notify_parent(p, p->exit_signal); @@ -754,12 +799,15 @@ reparent_thread(struct task_struct *p, struct task_struct *father, int traced) static void forget_original_parent(struct task_struct *father) { struct task_struct *p, *n, *reaper = father; - struct list_head ptrace_dead; - - INIT_LIST_HEAD(&ptrace_dead); + LIST_HEAD(ptrace_dead); write_lock_irq(&tasklist_lock); + /* + * First clean up ptrace if we were using it. + */ + ptrace_exit(father, &ptrace_dead); + do { reaper = next_thread(reaper); if (reaper == father) { @@ -768,58 +816,19 @@ static void forget_original_parent(struct task_struct *father) } } while (reaper->flags & PF_EXITING); - /* - * There are only two places where our children can be: - * - * - in our child list - * - in our ptraced child list - * - * Search them and reparent children. - */ list_for_each_entry_safe(p, n, &father->children, sibling) { - int ptrace; - - ptrace = p->ptrace; - - /* if father isn't the real parent, then ptrace must be enabled */ - BUG_ON(father != p->real_parent && !ptrace); - - if (father == p->real_parent) { - /* reparent with a reaper, real father it's us */ - p->real_parent = reaper; - reparent_thread(p, father, 0); - } else { - /* reparent ptraced task to its real parent */ - __ptrace_unlink (p); - if (p->exit_state == EXIT_ZOMBIE && !task_detached(p) && - thread_group_empty(p)) - do_notify_parent(p, p->exit_signal); - } - - /* - * if the ptraced child is a detached zombie we must collect - * it before we exit, or it will remain zombie forever since - * we prevented it from self-reap itself while it was being - * traced by us, to be able to see it in wait4. - */ - if (unlikely(ptrace && p->exit_state == EXIT_ZOMBIE && task_detached(p))) - list_add(&p->ptrace_list, &ptrace_dead); - } - - list_for_each_entry_safe(p, n, &father->ptrace_children, ptrace_list) { p->real_parent = reaper; - reparent_thread(p, father, 1); + if (p->parent == father) { + BUG_ON(p->ptrace); + p->parent = p->real_parent; + } + reparent_thread(p, father); } write_unlock_irq(&tasklist_lock); BUG_ON(!list_empty(&father->children)); - BUG_ON(!list_empty(&father->ptrace_children)); - - list_for_each_entry_safe(p, n, &ptrace_dead, ptrace_list) { - list_del_init(&p->ptrace_list); - release_task(p); - } + ptrace_exit_finish(father, &ptrace_dead); } /* @@ -1180,13 +1189,6 @@ static int eligible_child(enum pid_type type, struct pid *pid, int options, return 0; } - /* - * Do not consider detached threads that are - * not ptraced: - */ - if (task_detached(p) && !p->ptrace) - return 0; - /* Wait for all children (clone and not) if __WALL is set; * otherwise, wait for clone children *only* if __WCLONE is * set; otherwise, wait for non-clone children *only*. (Note: @@ -1399,7 +1401,7 @@ static int wait_task_zombie(struct task_struct *p, int options, * the lock and this task is uninteresting. If we return nonzero, we have * released the lock and the system call should return. */ -static int wait_task_stopped(struct task_struct *p, +static int wait_task_stopped(int ptrace, struct task_struct *p, int options, struct siginfo __user *infop, int __user *stat_addr, struct rusage __user *ru) { @@ -1407,7 +1409,7 @@ static int wait_task_stopped(struct task_struct *p, uid_t uid = 0; /* unneeded, required by compiler */ pid_t pid; - if (!(p->ptrace & PT_PTRACED) && !(options & WUNTRACED)) + if (!(options & WUNTRACED)) return 0; exit_code = 0; @@ -1416,7 +1418,7 @@ static int wait_task_stopped(struct task_struct *p, if (unlikely(!task_is_stopped_or_traced(p))) goto unlock_sig; - if (!(p->ptrace & PT_PTRACED) && p->signal->group_stop_count > 0) + if (!ptrace && p->signal->group_stop_count > 0) /* * A group stop is in progress and this is the group leader. * We won't report until all threads have stopped. @@ -1445,7 +1447,7 @@ unlock_sig: */ get_task_struct(p); pid = task_pid_vnr(p); - why = (p->ptrace & PT_PTRACED) ? CLD_TRAPPED : CLD_STOPPED; + why = ptrace ? CLD_TRAPPED : CLD_STOPPED; read_unlock(&tasklist_lock); if (unlikely(options & WNOWAIT)) @@ -1536,7 +1538,7 @@ static int wait_task_continued(struct task_struct *p, int options, * Returns zero if the search for a child should continue; * then *@notask_error is 0 if @p is an eligible child, or still -ECHILD. */ -static int wait_consider_task(struct task_struct *parent, +static int wait_consider_task(struct task_struct *parent, int ptrace, struct task_struct *p, int *notask_error, enum pid_type type, struct pid *pid, int options, struct siginfo __user *infop, @@ -1546,6 +1548,15 @@ static int wait_consider_task(struct task_struct *parent, if (ret <= 0) return ret; + if (likely(!ptrace) && unlikely(p->ptrace)) { + /* + * This child is hidden by ptrace. + * We aren't allowed to see it now, but eventually we will. + */ + *notask_error = 0; + return 0; + } + if (p->exit_state == EXIT_DEAD) return 0; @@ -1562,7 +1573,8 @@ static int wait_consider_task(struct task_struct *parent, *notask_error = 0; if (task_is_stopped_or_traced(p)) - return wait_task_stopped(p, options, infop, stat_addr, ru); + return wait_task_stopped(ptrace, p, options, + infop, stat_addr, ru); return wait_task_continued(p, options, infop, stat_addr, ru); } @@ -1583,11 +1595,16 @@ static int do_wait_thread(struct task_struct *tsk, int *notask_error, struct task_struct *p; list_for_each_entry(p, &tsk->children, sibling) { - int ret = wait_consider_task(tsk, p, notask_error, - type, pid, options, - infop, stat_addr, ru); - if (ret) - return ret; + /* + * Do not consider detached threads. + */ + if (!task_detached(p)) { + int ret = wait_consider_task(tsk, 0, p, notask_error, + type, pid, options, + infop, stat_addr, ru); + if (ret) + return ret; + } } return 0; @@ -1601,21 +1618,16 @@ static int ptrace_do_wait(struct task_struct *tsk, int *notask_error, struct task_struct *p; /* - * If we never saw an eligile child, check for children stolen by - * ptrace. We don't leave -ECHILD in *@notask_error if there are any, - * because we will eventually be allowed to wait for them again. + * Traditionally we see ptrace'd stopped tasks regardless of options. */ - if (!*notask_error) - return 0; + options |= WUNTRACED; - list_for_each_entry(p, &tsk->ptrace_children, ptrace_list) { - int ret = eligible_child(type, pid, options, p); - if (unlikely(ret < 0)) + list_for_each_entry(p, &tsk->ptraced, ptrace_entry) { + int ret = wait_consider_task(tsk, 1, p, notask_error, + type, pid, options, + infop, stat_addr, ru); + if (ret) return ret; - if (ret) { - *notask_error = 0; - return 0; - } } return 0; diff --git a/kernel/fork.c b/kernel/fork.c index 4bd2f516401f..adefc1131f27 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1125,8 +1125,8 @@ static struct task_struct *copy_process(unsigned long clone_flags, */ p->group_leader = p; INIT_LIST_HEAD(&p->thread_group); - INIT_LIST_HEAD(&p->ptrace_children); - INIT_LIST_HEAD(&p->ptrace_list); + INIT_LIST_HEAD(&p->ptrace_entry); + INIT_LIST_HEAD(&p->ptraced); /* Now that the task is set up, run cgroup callbacks if * necessary. We need to run them before the task is visible @@ -1198,7 +1198,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, } if (likely(p->pid)) { - add_parent(p); + list_add_tail(&p->sibling, &p->real_parent->children); if (unlikely(p->ptrace & PT_PTRACED)) __ptrace_link(p, current->parent); diff --git a/kernel/ptrace.c b/kernel/ptrace.c index e337390fce01..8392a9da6450 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -33,13 +33,9 @@ */ void __ptrace_link(struct task_struct *child, struct task_struct *new_parent) { - BUG_ON(!list_empty(&child->ptrace_list)); - if (child->parent == new_parent) - return; - list_add(&child->ptrace_list, &child->parent->ptrace_children); - remove_parent(child); + BUG_ON(!list_empty(&child->ptrace_entry)); + list_add(&child->ptrace_entry, &new_parent->ptraced); child->parent = new_parent; - add_parent(child); } /* @@ -73,12 +69,8 @@ void __ptrace_unlink(struct task_struct *child) BUG_ON(!child->ptrace); child->ptrace = 0; - if (ptrace_reparented(child)) { - list_del_init(&child->ptrace_list); - remove_parent(child); - child->parent = child->real_parent; - add_parent(child); - } + child->parent = child->real_parent; + list_del_init(&child->ptrace_entry); if (task_is_traced(child)) ptrace_untrace(child); @@ -492,15 +484,34 @@ int ptrace_traceme(void) /* * Are we already being traced? */ +repeat: task_lock(current); if (!(current->ptrace & PT_PTRACED)) { + /* + * See ptrace_attach() comments about the locking here. + */ + unsigned long flags; + if (!write_trylock_irqsave(&tasklist_lock, flags)) { + task_unlock(current); + do { + cpu_relax(); + } while (!write_can_lock(&tasklist_lock)); + goto repeat; + } + ret = security_ptrace(current->parent, current, PTRACE_MODE_ATTACH); + /* * Set the ptrace bit in the process ptrace flags. + * Then link us on our parent's ptraced list. */ - if (!ret) + if (!ret) { current->ptrace |= PT_PTRACED; + __ptrace_link(current, current->real_parent); + } + + write_unlock_irqrestore(&tasklist_lock, flags); } task_unlock(current); return ret; -- cgit v1.2.3 From f89ab8619e5320cc9c2576f5f8dcbaf6c0ba3950 Mon Sep 17 00:00:00 2001 From: Joel Becker Date: Thu, 17 Jul 2008 14:53:48 -0700 Subject: Revert "configfs: Allow ->make_item() and ->make_group() to return detailed errors." This reverts commit 11c3b79218390a139f2d474ee1e983a672d5839a. The code will move to PTR_ERR(). Signed-off-by: Joel Becker --- Documentation/filesystems/configfs/configfs.txt | 10 ++--- .../filesystems/configfs/configfs_example.c | 14 +++---- drivers/net/netconsole.c | 10 ++--- fs/configfs/dir.c | 13 ++++--- fs/dlm/config.c | 45 ++++++++-------------- fs/ocfs2/cluster/heartbeat.c | 17 ++++---- fs/ocfs2/cluster/nodemanager.c | 45 ++++++++-------------- include/linux/configfs.h | 4 +- 8 files changed, 64 insertions(+), 94 deletions(-) (limited to 'include/linux') diff --git a/Documentation/filesystems/configfs/configfs.txt b/Documentation/filesystems/configfs/configfs.txt index 15838d706ea2..44c97e6accb2 100644 --- a/Documentation/filesystems/configfs/configfs.txt +++ b/Documentation/filesystems/configfs/configfs.txt @@ -233,12 +233,10 @@ accomplished via the group operations specified on the group's config_item_type. struct configfs_group_operations { - int (*make_item)(struct config_group *group, - const char *name, - struct config_item **new_item); - int (*make_group)(struct config_group *group, - const char *name, - struct config_group **new_group); + struct config_item *(*make_item)(struct config_group *group, + const char *name); + struct config_group *(*make_group)(struct config_group *group, + const char *name); int (*commit_item)(struct config_item *item); void (*disconnect_notify)(struct config_group *group, struct config_item *item); diff --git a/Documentation/filesystems/configfs/configfs_example.c b/Documentation/filesystems/configfs/configfs_example.c index 0b422acd470c..25151fd5c2c6 100644 --- a/Documentation/filesystems/configfs/configfs_example.c +++ b/Documentation/filesystems/configfs/configfs_example.c @@ -273,13 +273,13 @@ static inline struct simple_children *to_simple_children(struct config_item *ite return item ? container_of(to_config_group(item), struct simple_children, group) : NULL; } -static int simple_children_make_item(struct config_group *group, const char *name, struct config_item **new_item) +static struct config_item *simple_children_make_item(struct config_group *group, const char *name) { struct simple_child *simple_child; simple_child = kzalloc(sizeof(struct simple_child), GFP_KERNEL); if (!simple_child) - return -ENOMEM; + return NULL; config_item_init_type_name(&simple_child->item, name, @@ -287,8 +287,7 @@ static int simple_children_make_item(struct config_group *group, const char *nam simple_child->storeme = 0; - *new_item = &simple_child->item; - return 0; + return &simple_child->item; } static struct configfs_attribute simple_children_attr_description = { @@ -360,21 +359,20 @@ static struct configfs_subsystem simple_children_subsys = { * children of its own. */ -static int group_children_make_group(struct config_group *group, const char *name, struct config_group **new_group) +static struct config_group *group_children_make_group(struct config_group *group, const char *name) { struct simple_children *simple_children; simple_children = kzalloc(sizeof(struct simple_children), GFP_KERNEL); if (!simple_children) - return -ENOMEM; + return NULL; config_group_init_type_name(&simple_children->group, name, &simple_children_type); - *new_group = &simple_children->group; - return 0; + return &simple_children->group; } static struct configfs_attribute group_children_attr_description = { diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index 387a13395015..665341e43055 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -585,9 +585,8 @@ static struct config_item_type netconsole_target_type = { * Group operations and type for netconsole_subsys. */ -static int make_netconsole_target(struct config_group *group, - const char *name, - struct config_item **new_item) +static struct config_item *make_netconsole_target(struct config_group *group, + const char *name) { unsigned long flags; struct netconsole_target *nt; @@ -599,7 +598,7 @@ static int make_netconsole_target(struct config_group *group, nt = kzalloc(sizeof(*nt), GFP_KERNEL); if (!nt) { printk(KERN_ERR "netconsole: failed to allocate memory\n"); - return -ENOMEM; + return NULL; } nt->np.name = "netconsole"; @@ -616,8 +615,7 @@ static int make_netconsole_target(struct config_group *group, list_add(&nt->list, &target_list); spin_unlock_irqrestore(&target_list_lock, flags); - *new_item = &nt->item; - return 0; + return &nt->item; } static void drop_netconsole_target(struct config_group *group, diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 0e64312a084c..614e382a6049 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -1073,24 +1073,25 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) group = NULL; item = NULL; if (type->ct_group_ops->make_group) { - ret = type->ct_group_ops->make_group(to_config_group(parent_item), name, &group); - if (!ret) { + group = type->ct_group_ops->make_group(to_config_group(parent_item), name); + if (group) { link_group(to_config_group(parent_item), group); item = &group->cg_item; } } else { - ret = type->ct_group_ops->make_item(to_config_group(parent_item), name, &item); - if (!ret) + item = type->ct_group_ops->make_item(to_config_group(parent_item), name); + if (item) link_obj(parent_item, item); } mutex_unlock(&subsys->su_mutex); kfree(name); - if (ret) { + if (!item) { /* - * If ret != 0, then link_obj() was never called. + * If item == NULL, then link_obj() was never called. * There are no extra references to clean up. */ + ret = -ENOMEM; goto out_put; } diff --git a/fs/dlm/config.c b/fs/dlm/config.c index 492d8caaaf25..eac23bd288b2 100644 --- a/fs/dlm/config.c +++ b/fs/dlm/config.c @@ -41,20 +41,16 @@ struct comm; struct nodes; struct node; -static int make_cluster(struct config_group *, const char *, - struct config_group **); +static struct config_group *make_cluster(struct config_group *, const char *); static void drop_cluster(struct config_group *, struct config_item *); static void release_cluster(struct config_item *); -static int make_space(struct config_group *, const char *, - struct config_group **); +static struct config_group *make_space(struct config_group *, const char *); static void drop_space(struct config_group *, struct config_item *); static void release_space(struct config_item *); -static int make_comm(struct config_group *, const char *, - struct config_item **); +static struct config_item *make_comm(struct config_group *, const char *); static void drop_comm(struct config_group *, struct config_item *); static void release_comm(struct config_item *); -static int make_node(struct config_group *, const char *, - struct config_item **); +static struct config_item *make_node(struct config_group *, const char *); static void drop_node(struct config_group *, struct config_item *); static void release_node(struct config_item *); @@ -396,8 +392,8 @@ static struct node *to_node(struct config_item *i) return i ? container_of(i, struct node, item) : NULL; } -static int make_cluster(struct config_group *g, const char *name, - struct config_group **new_g) +static struct config_group *make_cluster(struct config_group *g, + const char *name) { struct cluster *cl = NULL; struct spaces *sps = NULL; @@ -435,15 +431,14 @@ static int make_cluster(struct config_group *g, const char *name, space_list = &sps->ss_group; comm_list = &cms->cs_group; - *new_g = &cl->group; - return 0; + return &cl->group; fail: kfree(cl); kfree(gps); kfree(sps); kfree(cms); - return -ENOMEM; + return NULL; } static void drop_cluster(struct config_group *g, struct config_item *i) @@ -471,8 +466,7 @@ static void release_cluster(struct config_item *i) kfree(cl); } -static int make_space(struct config_group *g, const char *name, - struct config_group **new_g) +static struct config_group *make_space(struct config_group *g, const char *name) { struct space *sp = NULL; struct nodes *nds = NULL; @@ -495,14 +489,13 @@ static int make_space(struct config_group *g, const char *name, INIT_LIST_HEAD(&sp->members); mutex_init(&sp->members_lock); sp->members_count = 0; - *new_g = &sp->group; - return 0; + return &sp->group; fail: kfree(sp); kfree(gps); kfree(nds); - return -ENOMEM; + return NULL; } static void drop_space(struct config_group *g, struct config_item *i) @@ -529,21 +522,19 @@ static void release_space(struct config_item *i) kfree(sp); } -static int make_comm(struct config_group *g, const char *name, - struct config_item **new_i) +static struct config_item *make_comm(struct config_group *g, const char *name) { struct comm *cm; cm = kzalloc(sizeof(struct comm), GFP_KERNEL); if (!cm) - return -ENOMEM; + return NULL; config_item_init_type_name(&cm->item, name, &comm_type); cm->nodeid = -1; cm->local = 0; cm->addr_count = 0; - *new_i = &cm->item; - return 0; + return &cm->item; } static void drop_comm(struct config_group *g, struct config_item *i) @@ -563,15 +554,14 @@ static void release_comm(struct config_item *i) kfree(cm); } -static int make_node(struct config_group *g, const char *name, - struct config_item **new_i) +static struct config_item *make_node(struct config_group *g, const char *name) { struct space *sp = to_space(g->cg_item.ci_parent); struct node *nd; nd = kzalloc(sizeof(struct node), GFP_KERNEL); if (!nd) - return -ENOMEM; + return NULL; config_item_init_type_name(&nd->item, name, &node_type); nd->nodeid = -1; @@ -583,8 +573,7 @@ static int make_node(struct config_group *g, const char *name, sp->members_count++; mutex_unlock(&sp->members_lock); - *new_i = &nd->item; - return 0; + return &nd->item; } static void drop_node(struct config_group *g, struct config_item *i) diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index 443d108211ab..f02ccb34604d 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -1489,28 +1489,25 @@ static struct o2hb_heartbeat_group *to_o2hb_heartbeat_group(struct config_group : NULL; } -static int o2hb_heartbeat_group_make_item(struct config_group *group, - const char *name, - struct config_item **new_item) +static struct config_item *o2hb_heartbeat_group_make_item(struct config_group *group, + const char *name) { struct o2hb_region *reg = NULL; - int ret = 0; + struct config_item *ret = NULL; reg = kzalloc(sizeof(struct o2hb_region), GFP_KERNEL); - if (reg == NULL) { - ret = -ENOMEM; - goto out; - } + if (reg == NULL) + goto out; /* ENOMEM */ config_item_init_type_name(®->hr_item, name, &o2hb_region_type); - *new_item = ®->hr_item; + ret = ®->hr_item; spin_lock(&o2hb_live_lock); list_add_tail(®->hr_all_item, &o2hb_all_regions); spin_unlock(&o2hb_live_lock); out: - if (ret) + if (ret == NULL) kfree(reg); return ret; diff --git a/fs/ocfs2/cluster/nodemanager.c b/fs/ocfs2/cluster/nodemanager.c index b364b7052e46..cfdb08b484ed 100644 --- a/fs/ocfs2/cluster/nodemanager.c +++ b/fs/ocfs2/cluster/nodemanager.c @@ -644,32 +644,27 @@ out: return ret; } -static int o2nm_node_group_make_item(struct config_group *group, - const char *name, - struct config_item **new_item) +static struct config_item *o2nm_node_group_make_item(struct config_group *group, + const char *name) { struct o2nm_node *node = NULL; - int ret = 0; + struct config_item *ret = NULL; - if (strlen(name) > O2NM_MAX_NAME_LEN) { - ret = -ENAMETOOLONG; - goto out; - } + if (strlen(name) > O2NM_MAX_NAME_LEN) + goto out; /* ENAMETOOLONG */ node = kzalloc(sizeof(struct o2nm_node), GFP_KERNEL); - if (node == NULL) { - ret = -ENOMEM; - goto out; - } + if (node == NULL) + goto out; /* ENOMEM */ strcpy(node->nd_name, name); /* use item.ci_namebuf instead? */ config_item_init_type_name(&node->nd_item, name, &o2nm_node_type); spin_lock_init(&node->nd_lock); - *new_item = &node->nd_item; + ret = &node->nd_item; out: - if (ret) + if (ret == NULL) kfree(node); return ret; @@ -756,31 +751,25 @@ static struct o2nm_cluster_group *to_o2nm_cluster_group(struct config_group *gro } #endif -static int o2nm_cluster_group_make_group(struct config_group *group, - const char *name, - struct config_group **new_group) +static struct config_group *o2nm_cluster_group_make_group(struct config_group *group, + const char *name) { struct o2nm_cluster *cluster = NULL; struct o2nm_node_group *ns = NULL; - struct config_group *o2hb_group = NULL; + struct config_group *o2hb_group = NULL, *ret = NULL; void *defs = NULL; - int ret = 0; /* this runs under the parent dir's i_mutex; there can be only * one caller in here at a time */ - if (o2nm_single_cluster) { - ret = -ENOSPC; - goto out; - } + if (o2nm_single_cluster) + goto out; /* ENOSPC */ cluster = kzalloc(sizeof(struct o2nm_cluster), GFP_KERNEL); ns = kzalloc(sizeof(struct o2nm_node_group), GFP_KERNEL); defs = kcalloc(3, sizeof(struct config_group *), GFP_KERNEL); o2hb_group = o2hb_alloc_hb_set(); - if (cluster == NULL || ns == NULL || o2hb_group == NULL || defs == NULL) { - ret = -ENOMEM; + if (cluster == NULL || ns == NULL || o2hb_group == NULL || defs == NULL) goto out; - } config_group_init_type_name(&cluster->cl_group, name, &o2nm_cluster_type); @@ -797,11 +786,11 @@ static int o2nm_cluster_group_make_group(struct config_group *group, cluster->cl_idle_timeout_ms = O2NET_IDLE_TIMEOUT_MS_DEFAULT; cluster->cl_keepalive_delay_ms = O2NET_KEEPALIVE_DELAY_MS_DEFAULT; - *new_group = &cluster->cl_group; + ret = &cluster->cl_group; o2nm_single_cluster = cluster; out: - if (ret) { + if (ret == NULL) { kfree(cluster); kfree(ns); o2hb_free_hb_set(o2hb_group); diff --git a/include/linux/configfs.h b/include/linux/configfs.h index 0488f937634a..3ae65b1bf90f 100644 --- a/include/linux/configfs.h +++ b/include/linux/configfs.h @@ -165,8 +165,8 @@ struct configfs_item_operations { }; struct configfs_group_operations { - int (*make_item)(struct config_group *group, const char *name, struct config_item **new_item); - int (*make_group)(struct config_group *group, const char *name, struct config_group **new_group); + struct config_item *(*make_item)(struct config_group *group, const char *name); + struct config_group *(*make_group)(struct config_group *group, const char *name); int (*commit_item)(struct config_item *item); void (*disconnect_notify)(struct config_group *group, struct config_item *item); void (*drop_item)(struct config_group *group, struct config_item *item); -- cgit v1.2.3 From a6795e9ebb420d87af43789174689af0d66d1d35 Mon Sep 17 00:00:00 2001 From: Joel Becker Date: Thu, 17 Jul 2008 15:21:29 -0700 Subject: configfs: Allow ->make_item() and ->make_group() to return detailed errors. The configfs operations ->make_item() and ->make_group() currently return a new item/group. A return of NULL signifies an error. Because of this, -ENOMEM is the only return code bubbled up the stack. Multiple folks have requested the ability to return specific error codes when these operations fail. This patch adds that ability by changing the ->make_item/group() ops to return ERR_PTR() values. These errors are bubbled up appropriately. NULL returns are changed to -ENOMEM for compatibility. Also updated are the in-kernel users of configfs. This is a rework of reverted commit 11c3b79218390a139f2d474ee1e983a672d5839a. Signed-off-by: Joel Becker --- .../filesystems/configfs/configfs_example.c | 4 ++-- drivers/net/netconsole.c | 2 +- fs/configfs/dir.c | 25 +++++++++++++--------- fs/dlm/config.c | 8 +++---- fs/ocfs2/cluster/heartbeat.c | 10 ++------- fs/ocfs2/cluster/nodemanager.c | 16 +++++--------- include/linux/configfs.h | 3 ++- 7 files changed, 31 insertions(+), 37 deletions(-) (limited to 'include/linux') diff --git a/Documentation/filesystems/configfs/configfs_example.c b/Documentation/filesystems/configfs/configfs_example.c index 25151fd5c2c6..039648791701 100644 --- a/Documentation/filesystems/configfs/configfs_example.c +++ b/Documentation/filesystems/configfs/configfs_example.c @@ -279,7 +279,7 @@ static struct config_item *simple_children_make_item(struct config_group *group, simple_child = kzalloc(sizeof(struct simple_child), GFP_KERNEL); if (!simple_child) - return NULL; + return ERR_PTR(-ENOMEM); config_item_init_type_name(&simple_child->item, name, @@ -366,7 +366,7 @@ static struct config_group *group_children_make_group(struct config_group *group simple_children = kzalloc(sizeof(struct simple_children), GFP_KERNEL); if (!simple_children) - return NULL; + return ERR_PTR(-ENOMEM); config_group_init_type_name(&simple_children->group, name, diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index 665341e43055..e13966bb5f77 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -598,7 +598,7 @@ static struct config_item *make_netconsole_target(struct config_group *group, nt = kzalloc(sizeof(*nt), GFP_KERNEL); if (!nt) { printk(KERN_ERR "netconsole: failed to allocate memory\n"); - return NULL; + return ERR_PTR(-ENOMEM); } nt->np.name = "netconsole"; diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 614e382a6049..179589be063a 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -1027,9 +1027,10 @@ EXPORT_SYMBOL(configfs_undepend_item); static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) { - int ret, module_got = 0; - struct config_group *group; - struct config_item *item; + int ret = 0; + int module_got = 0; + struct config_group *group = NULL; + struct config_item *item = NULL; struct config_item *parent_item; struct configfs_subsystem *subsys; struct configfs_dirent *sd; @@ -1070,28 +1071,32 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) snprintf(name, dentry->d_name.len + 1, "%s", dentry->d_name.name); mutex_lock(&subsys->su_mutex); - group = NULL; - item = NULL; if (type->ct_group_ops->make_group) { group = type->ct_group_ops->make_group(to_config_group(parent_item), name); - if (group) { + if (!group) + group = ERR_PTR(-ENOMEM); + if (!IS_ERR(group)) { link_group(to_config_group(parent_item), group); item = &group->cg_item; - } + } else + ret = PTR_ERR(group); } else { item = type->ct_group_ops->make_item(to_config_group(parent_item), name); - if (item) + if (!item) + item = ERR_PTR(-ENOMEM); + if (!IS_ERR(item)) link_obj(parent_item, item); + else + ret = PTR_ERR(item); } mutex_unlock(&subsys->su_mutex); kfree(name); - if (!item) { + if (ret) { /* * If item == NULL, then link_obj() was never called. * There are no extra references to clean up. */ - ret = -ENOMEM; goto out_put; } diff --git a/fs/dlm/config.c b/fs/dlm/config.c index eac23bd288b2..c4e7d721bd8d 100644 --- a/fs/dlm/config.c +++ b/fs/dlm/config.c @@ -438,7 +438,7 @@ static struct config_group *make_cluster(struct config_group *g, kfree(gps); kfree(sps); kfree(cms); - return NULL; + return ERR_PTR(-ENOMEM); } static void drop_cluster(struct config_group *g, struct config_item *i) @@ -495,7 +495,7 @@ static struct config_group *make_space(struct config_group *g, const char *name) kfree(sp); kfree(gps); kfree(nds); - return NULL; + return ERR_PTR(-ENOMEM); } static void drop_space(struct config_group *g, struct config_item *i) @@ -528,7 +528,7 @@ static struct config_item *make_comm(struct config_group *g, const char *name) cm = kzalloc(sizeof(struct comm), GFP_KERNEL); if (!cm) - return NULL; + return ERR_PTR(-ENOMEM); config_item_init_type_name(&cm->item, name, &comm_type); cm->nodeid = -1; @@ -561,7 +561,7 @@ static struct config_item *make_node(struct config_group *g, const char *name) nd = kzalloc(sizeof(struct node), GFP_KERNEL); if (!nd) - return NULL; + return ERR_PTR(-ENOMEM); config_item_init_type_name(&nd->item, name, &node_type); nd->nodeid = -1; diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index f02ccb34604d..7dce1612553e 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -1493,24 +1493,18 @@ static struct config_item *o2hb_heartbeat_group_make_item(struct config_group *g const char *name) { struct o2hb_region *reg = NULL; - struct config_item *ret = NULL; reg = kzalloc(sizeof(struct o2hb_region), GFP_KERNEL); if (reg == NULL) - goto out; /* ENOMEM */ + return ERR_PTR(-ENOMEM); config_item_init_type_name(®->hr_item, name, &o2hb_region_type); - ret = ®->hr_item; - spin_lock(&o2hb_live_lock); list_add_tail(®->hr_all_item, &o2hb_all_regions); spin_unlock(&o2hb_live_lock); -out: - if (ret == NULL) - kfree(reg); - return ret; + return ®->hr_item; } static void o2hb_heartbeat_group_drop_item(struct config_group *group, diff --git a/fs/ocfs2/cluster/nodemanager.c b/fs/ocfs2/cluster/nodemanager.c index cfdb08b484ed..816a3f61330c 100644 --- a/fs/ocfs2/cluster/nodemanager.c +++ b/fs/ocfs2/cluster/nodemanager.c @@ -648,26 +648,19 @@ static struct config_item *o2nm_node_group_make_item(struct config_group *group, const char *name) { struct o2nm_node *node = NULL; - struct config_item *ret = NULL; if (strlen(name) > O2NM_MAX_NAME_LEN) - goto out; /* ENAMETOOLONG */ + return ERR_PTR(-ENAMETOOLONG); node = kzalloc(sizeof(struct o2nm_node), GFP_KERNEL); if (node == NULL) - goto out; /* ENOMEM */ + return ERR_PTR(-ENOMEM); strcpy(node->nd_name, name); /* use item.ci_namebuf instead? */ config_item_init_type_name(&node->nd_item, name, &o2nm_node_type); spin_lock_init(&node->nd_lock); - ret = &node->nd_item; - -out: - if (ret == NULL) - kfree(node); - - return ret; + return &node->nd_item; } static void o2nm_node_group_drop_item(struct config_group *group, @@ -762,7 +755,7 @@ static struct config_group *o2nm_cluster_group_make_group(struct config_group *g /* this runs under the parent dir's i_mutex; there can be only * one caller in here at a time */ if (o2nm_single_cluster) - goto out; /* ENOSPC */ + return ERR_PTR(-ENOSPC); cluster = kzalloc(sizeof(struct o2nm_cluster), GFP_KERNEL); ns = kzalloc(sizeof(struct o2nm_node_group), GFP_KERNEL); @@ -795,6 +788,7 @@ out: kfree(ns); o2hb_free_hb_set(o2hb_group); kfree(defs); + ret = ERR_PTR(-ENOMEM); } return ret; diff --git a/include/linux/configfs.h b/include/linux/configfs.h index 3ae65b1bf90f..d62c19ff041c 100644 --- a/include/linux/configfs.h +++ b/include/linux/configfs.h @@ -148,7 +148,8 @@ struct configfs_attribute { * items. If the item is a group, it may support mkdir(2). * Groups supply one of make_group() and make_item(). If the * group supports make_group(), one can create group children. If it - * supports make_item(), one can create config_item children. If it has + * supports make_item(), one can create config_item children. make_group() + * and make_item() return ERR_PTR() on errors. If it has * default_groups on group->default_groups, it has automatically created * group children. default_groups may coexist alongsize make_group() or * make_item(), but if the group wishes to have only default_groups -- cgit v1.2.3 From e8a0464cc950972824e2e128028ae3db666ec1ed Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 17 Jul 2008 00:34:19 -0700 Subject: netdev: Allocate multiple queues for TX. alloc_netdev_mq() now allocates an array of netdev_queue structures for TX, based upon the queue_count argument. Furthermore, all accesses to the TX queues are now vectored through the netdev_get_tx_queue() and netdev_for_each_tx_queue() interfaces. This makes it easy to grep the tree for all things that want to get to a TX queue of a net device. Problem spots which are not really multiqueue aware yet, and only work with one queue, can easily be spotted by grepping for all netdev_get_tx_queue() calls that pass in a zero index. Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 6 +- drivers/net/hamradio/bpqether.c | 6 +- drivers/net/ifb.c | 12 ++- drivers/net/macvlan.c | 6 +- drivers/net/wireless/hostap/hostap_hw.c | 6 +- include/linux/netdevice.h | 69 ++++++++----- include/net/sch_generic.h | 37 +++++-- net/8021q/vlan_dev.c | 10 +- net/core/dev.c | 40 +++++-- net/core/rtnetlink.c | 2 +- net/mac80211/main.c | 4 +- net/mac80211/wme.c | 12 +-- net/netrom/af_netrom.c | 6 +- net/rose/af_rose.c | 6 +- net/sched/cls_api.c | 4 +- net/sched/sch_api.c | 32 ++++-- net/sched/sch_generic.c | 178 +++++++++++++++++++++++--------- net/sched/sch_teql.c | 21 ++-- 18 files changed, 320 insertions(+), 137 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index fd87dbe7999a..9737c06045d6 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -5042,7 +5042,9 @@ static int bond_check_params(struct bond_params *params) static struct lock_class_key bonding_netdev_xmit_lock_key; -static void bond_set_lockdep_class_one(struct netdev_queue *txq) +static void bond_set_lockdep_class_one(struct net_device *dev, + struct netdev_queue *txq, + void *_unused) { lockdep_set_class(&txq->_xmit_lock, &bonding_netdev_xmit_lock_key); @@ -5050,7 +5052,7 @@ static void bond_set_lockdep_class_one(struct netdev_queue *txq) static void bond_set_lockdep_class(struct net_device *dev) { - bond_set_lockdep_class_one(&dev->tx_queue); + netdev_for_each_tx_queue(dev, bond_set_lockdep_class_one, NULL); } /* Create a new bond based on the specified name and bonding parameters. diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c index fb186b8c3d4d..b6500b2aacf2 100644 --- a/drivers/net/hamradio/bpqether.c +++ b/drivers/net/hamradio/bpqether.c @@ -124,14 +124,16 @@ static LIST_HEAD(bpq_devices); */ static struct lock_class_key bpq_netdev_xmit_lock_key; -static void bpq_set_lockdep_class_one(struct netdev_queue *txq) +static void bpq_set_lockdep_class_one(struct net_device *dev, + struct netdev_queue *txq, + void *_unused) { lockdep_set_class(&txq->_xmit_lock, &bpq_netdev_xmit_lock_key); } static void bpq_set_lockdep_class(struct net_device *dev) { - bpq_set_lockdep_class_one(&dev->tx_queue); + netdev_for_each_tx_queue(dev, bpq_set_lockdep_class_one, NULL); } /* ------------------------------------------------------------------------ */ diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c index ccbd6554f6eb..897b05e79ed0 100644 --- a/drivers/net/ifb.c +++ b/drivers/net/ifb.c @@ -229,14 +229,20 @@ module_param(numifbs, int, 0); MODULE_PARM_DESC(numifbs, "Number of ifb devices"); /* - * dev_ifb->tx_queue.lock is usually taken after dev->rx_queue.lock, + * dev_ifb's TX queue lock is usually taken after dev->rx_queue.lock, * reversely to e.g. qdisc_lock_tree(). It should be safe until - * ifb doesn't take dev->tx_queue.lock with dev_ifb->rx_queue.lock. + * ifb doesn't take dev's TX queue lock with dev_ifb->rx_queue.lock. * But lockdep should know that ifb has different locks from dev. */ static struct lock_class_key ifb_tx_queue_lock_key; static struct lock_class_key ifb_rx_queue_lock_key; +static void set_tx_lockdep_key(struct net_device *dev, + struct netdev_queue *txq, + void *_unused) +{ + lockdep_set_class(&txq->lock, &ifb_tx_queue_lock_key); +} static int __init ifb_init_one(int index) { @@ -258,7 +264,7 @@ static int __init ifb_init_one(int index) if (err < 0) goto err; - lockdep_set_class(&dev_ifb->tx_queue.lock, &ifb_tx_queue_lock_key); + netdev_for_each_tx_queue(dev_ifb, set_tx_lockdep_key, NULL); lockdep_set_class(&dev_ifb->rx_queue.lock, &ifb_rx_queue_lock_key); return 0; diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 980001c2cf96..72745ce588c6 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -285,7 +285,9 @@ static struct lock_class_key macvlan_netdev_xmit_lock_key; #define MACVLAN_STATE_MASK \ ((1<<__LINK_STATE_NOCARRIER) | (1<<__LINK_STATE_DORMANT)) -static void macvlan_set_lockdep_class_one(struct netdev_queue *txq) +static void macvlan_set_lockdep_class_one(struct net_device *dev, + struct netdev_queue *txq, + void *_unused) { lockdep_set_class(&txq->_xmit_lock, &macvlan_netdev_xmit_lock_key); @@ -293,7 +295,7 @@ static void macvlan_set_lockdep_class_one(struct netdev_queue *txq) static void macvlan_set_lockdep_class(struct net_device *dev) { - macvlan_set_lockdep_class_one(&dev->tx_queue); + netdev_for_each_tx_queue(dev, macvlan_set_lockdep_class_one, NULL); } static int macvlan_init(struct net_device *dev) diff --git a/drivers/net/wireless/hostap/hostap_hw.c b/drivers/net/wireless/hostap/hostap_hw.c index c1f4bb005d92..13d5882f1f21 100644 --- a/drivers/net/wireless/hostap/hostap_hw.c +++ b/drivers/net/wireless/hostap/hostap_hw.c @@ -3102,7 +3102,9 @@ static void prism2_clear_set_tim_queue(local_info_t *local) */ static struct lock_class_key hostap_netdev_xmit_lock_key; -static void prism2_set_lockdep_class_one(struct netdev_queue *txq) +static void prism2_set_lockdep_class_one(struct net_device *dev, + struct netdev_queue *txq, + void *_unused) { lockdep_set_class(&txq->_xmit_lock, &hostap_netdev_xmit_lock_key); @@ -3110,7 +3112,7 @@ static void prism2_set_lockdep_class_one(struct netdev_queue *txq) static void prism2_set_lockdep_class(struct net_device *dev) { - prism2_set_lockdep_class_one(&dev->tx_queue); + netdev_for_each_tx_queue(dev, prism2_set_lockdep_class_one, NULL); } static struct net_device * diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 570cf7affa72..f25d4f5a31b0 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -463,7 +463,7 @@ struct netdev_queue { struct Qdisc *qdisc_sleeping; struct list_head qdisc_list; struct netdev_queue *next_sched; -}; +} ____cacheline_aligned_in_smp; /* * The DEVICE structure. @@ -641,7 +641,9 @@ struct net_device unsigned char broadcast[MAX_ADDR_LEN]; /* hw bcast add */ struct netdev_queue rx_queue; - struct netdev_queue tx_queue ____cacheline_aligned_in_smp; + + struct netdev_queue *_tx ____cacheline_aligned_in_smp; + unsigned int num_tx_queues; unsigned long tx_queue_len; /* Max frames per queue allowed */ /* @@ -764,6 +766,25 @@ struct net_device #define NETDEV_ALIGN 32 #define NETDEV_ALIGN_CONST (NETDEV_ALIGN - 1) +static inline +struct netdev_queue *netdev_get_tx_queue(const struct net_device *dev, + unsigned int index) +{ + return &dev->_tx[index]; +} + +static inline void netdev_for_each_tx_queue(struct net_device *dev, + void (*f)(struct net_device *, + struct netdev_queue *, + void *), + void *arg) +{ + unsigned int i; + + for (i = 0; i < dev->num_tx_queues; i++) + f(dev, &dev->_tx[i], arg); +} + /* * Net namespace inlines */ @@ -977,7 +998,7 @@ static inline void netif_schedule_queue(struct netdev_queue *txq) static inline void netif_schedule(struct net_device *dev) { - netif_schedule_queue(&dev->tx_queue); + netif_schedule_queue(netdev_get_tx_queue(dev, 0)); } /** @@ -993,7 +1014,7 @@ static inline void netif_tx_start_queue(struct netdev_queue *dev_queue) static inline void netif_start_queue(struct net_device *dev) { - netif_tx_start_queue(&dev->tx_queue); + netif_tx_start_queue(netdev_get_tx_queue(dev, 0)); } /** @@ -1017,7 +1038,7 @@ static inline void netif_tx_wake_queue(struct netdev_queue *dev_queue) static inline void netif_wake_queue(struct net_device *dev) { - netif_tx_wake_queue(&dev->tx_queue); + netif_tx_wake_queue(netdev_get_tx_queue(dev, 0)); } /** @@ -1034,7 +1055,7 @@ static inline void netif_tx_stop_queue(struct netdev_queue *dev_queue) static inline void netif_stop_queue(struct net_device *dev) { - netif_tx_stop_queue(&dev->tx_queue); + netif_tx_stop_queue(netdev_get_tx_queue(dev, 0)); } /** @@ -1050,7 +1071,7 @@ static inline int netif_tx_queue_stopped(const struct netdev_queue *dev_queue) static inline int netif_queue_stopped(const struct net_device *dev) { - return netif_tx_queue_stopped(&dev->tx_queue); + return netif_tx_queue_stopped(netdev_get_tx_queue(dev, 0)); } /** @@ -1134,7 +1155,7 @@ static inline void netif_wake_subqueue(struct net_device *dev, u16 queue_index) #endif if (test_and_clear_bit(__QUEUE_STATE_XOFF, &dev->egress_subqueue[queue_index].state)) - __netif_schedule(&dev->tx_queue); + __netif_schedule(netdev_get_tx_queue(dev, 0)); } /** @@ -1430,18 +1451,19 @@ static inline void __netif_tx_lock(struct netdev_queue *txq, int cpu) static inline void netif_tx_lock(struct net_device *dev) { - __netif_tx_lock(&dev->tx_queue, smp_processor_id()); -} + int cpu = smp_processor_id(); + unsigned int i; -static inline void __netif_tx_lock_bh(struct netdev_queue *txq) -{ - spin_lock_bh(&txq->_xmit_lock); - txq->xmit_lock_owner = smp_processor_id(); + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + __netif_tx_lock(txq, cpu); + } } static inline void netif_tx_lock_bh(struct net_device *dev) { - __netif_tx_lock_bh(&dev->tx_queue); + local_bh_disable(); + netif_tx_lock(dev); } static inline int __netif_tx_trylock(struct netdev_queue *txq) @@ -1454,7 +1476,7 @@ static inline int __netif_tx_trylock(struct netdev_queue *txq) static inline int netif_tx_trylock(struct net_device *dev) { - return __netif_tx_trylock(&dev->tx_queue); + return __netif_tx_trylock(netdev_get_tx_queue(dev, 0)); } static inline void __netif_tx_unlock(struct netdev_queue *txq) @@ -1465,18 +1487,19 @@ static inline void __netif_tx_unlock(struct netdev_queue *txq) static inline void netif_tx_unlock(struct net_device *dev) { - __netif_tx_unlock(&dev->tx_queue); -} + unsigned int i; + + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + __netif_tx_unlock(txq); + } -static inline void __netif_tx_unlock_bh(struct netdev_queue *txq) -{ - txq->xmit_lock_owner = -1; - spin_unlock_bh(&txq->_xmit_lock); } static inline void netif_tx_unlock_bh(struct net_device *dev) { - __netif_tx_unlock_bh(&dev->tx_queue); + netif_tx_unlock(dev); + local_bh_enable(); } #define HARD_TX_LOCK(dev, txq, cpu) { \ diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 5ba66b555578..b47f556c66f8 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -230,32 +230,47 @@ extern void tcf_destroy_chain(struct tcf_proto **fl); /* Reset all TX qdiscs of a device. */ static inline void qdisc_reset_all_tx(struct net_device *dev) { - qdisc_reset(dev->tx_queue.qdisc); + unsigned int i; + for (i = 0; i < dev->num_tx_queues; i++) + qdisc_reset(netdev_get_tx_queue(dev, i)->qdisc); } /* Are all TX queues of the device empty? */ static inline bool qdisc_all_tx_empty(const struct net_device *dev) { - const struct netdev_queue *txq = &dev->tx_queue; - const struct Qdisc *q = txq->qdisc; + unsigned int i; + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + const struct Qdisc *q = txq->qdisc; - return (q->q.qlen == 0); + if (q->q.qlen) + return false; + } + return true; } /* Are any of the TX qdiscs changing? */ static inline bool qdisc_tx_changing(struct net_device *dev) { - struct netdev_queue *txq = &dev->tx_queue; - - return (txq->qdisc != txq->qdisc_sleeping); + unsigned int i; + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + if (txq->qdisc != txq->qdisc_sleeping) + return true; + } + return false; } -/* Is the device using the noop qdisc? */ +/* Is the device using the noop qdisc on all queues? */ static inline bool qdisc_tx_is_noop(const struct net_device *dev) { - const struct netdev_queue *txq = &dev->tx_queue; - - return (txq->qdisc == &noop_qdisc); + unsigned int i; + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + if (txq->qdisc != &noop_qdisc) + return false; + } + return true; } static inline int __qdisc_enqueue_tail(struct sk_buff *skb, struct Qdisc *sch, diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 6b985f23fd9f..f42bc2b26b85 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -570,16 +570,18 @@ static void vlan_dev_set_rx_mode(struct net_device *vlan_dev) */ static struct lock_class_key vlan_netdev_xmit_lock_key; -static void vlan_dev_set_lockdep_one(struct netdev_queue *txq, - int subclass) +static void vlan_dev_set_lockdep_one(struct net_device *dev, + struct netdev_queue *txq, + void *_subclass) { lockdep_set_class_and_subclass(&txq->_xmit_lock, - &vlan_netdev_xmit_lock_key, subclass); + &vlan_netdev_xmit_lock_key, + *(int *)_subclass); } static void vlan_dev_set_lockdep_class(struct net_device *dev, int subclass) { - vlan_dev_set_lockdep_one(&dev->tx_queue, subclass); + netdev_for_each_tx_queue(dev, vlan_dev_set_lockdep_one, &subclass); } static const struct header_ops vlan_header_ops = { diff --git a/net/core/dev.c b/net/core/dev.c index 9b49f74a9820..69378f250695 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1666,6 +1666,12 @@ out_kfree_skb: * --BLG */ +static struct netdev_queue *dev_pick_tx(struct net_device *dev, + struct sk_buff *skb) +{ + return netdev_get_tx_queue(dev, 0); +} + int dev_queue_xmit(struct sk_buff *skb) { struct net_device *dev = skb->dev; @@ -1702,7 +1708,7 @@ int dev_queue_xmit(struct sk_buff *skb) } gso: - txq = &dev->tx_queue; + txq = dev_pick_tx(dev, skb); spin_lock_prefetch(&txq->lock); /* Disable soft irqs for various locks below. Also @@ -3788,8 +3794,9 @@ static void rollback_registered(struct net_device *dev) dev_put(dev); } -static void __netdev_init_queue_locks_one(struct netdev_queue *dev_queue, - struct net_device *dev) +static void __netdev_init_queue_locks_one(struct net_device *dev, + struct netdev_queue *dev_queue, + void *_unused) { spin_lock_init(&dev_queue->_xmit_lock); netdev_set_lockdep_class(&dev_queue->_xmit_lock, dev->type); @@ -3798,8 +3805,8 @@ static void __netdev_init_queue_locks_one(struct netdev_queue *dev_queue, static void netdev_init_queue_locks(struct net_device *dev) { - __netdev_init_queue_locks_one(&dev->tx_queue, dev); - __netdev_init_queue_locks_one(&dev->rx_queue, dev); + netdev_for_each_tx_queue(dev, __netdev_init_queue_locks_one, NULL); + __netdev_init_queue_locks_one(dev, &dev->rx_queue, NULL); } /** @@ -4119,7 +4126,8 @@ static struct net_device_stats *internal_stats(struct net_device *dev) } static void netdev_init_one_queue(struct net_device *dev, - struct netdev_queue *queue) + struct netdev_queue *queue, + void *_unused) { spin_lock_init(&queue->lock); queue->dev = dev; @@ -4127,8 +4135,8 @@ static void netdev_init_one_queue(struct net_device *dev, static void netdev_init_queues(struct net_device *dev) { - netdev_init_one_queue(dev, &dev->rx_queue); - netdev_init_one_queue(dev, &dev->tx_queue); + netdev_init_one_queue(dev, &dev->rx_queue, NULL); + netdev_for_each_tx_queue(dev, netdev_init_one_queue, NULL); } /** @@ -4145,9 +4153,10 @@ static void netdev_init_queues(struct net_device *dev) struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, void (*setup)(struct net_device *), unsigned int queue_count) { - void *p; + struct netdev_queue *tx; struct net_device *dev; int alloc_size; + void *p; BUG_ON(strlen(name) >= sizeof(dev->name)); @@ -4167,11 +4176,22 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, return NULL; } + tx = kzalloc(sizeof(struct netdev_queue) * queue_count, GFP_KERNEL); + if (!tx) { + printk(KERN_ERR "alloc_netdev: Unable to allocate " + "tx qdiscs.\n"); + kfree(p); + return NULL; + } + dev = (struct net_device *) (((long)p + NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST); dev->padded = (char *)dev - (char *)p; dev_net_set(dev, &init_net); + dev->_tx = tx; + dev->num_tx_queues = queue_count; + if (sizeof_priv) { dev->priv = ((char *)dev + ((sizeof(struct net_device) + @@ -4205,6 +4225,8 @@ void free_netdev(struct net_device *dev) { release_net(dev_net(dev)); + kfree(dev->_tx); + /* Compatibility with error handling in drivers */ if (dev->reg_state == NETREG_UNINITIALIZED) { kfree((char *)dev - dev->padded); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 8ef9f1db610e..71edb8b36341 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -636,7 +636,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, if (dev->master) NLA_PUT_U32(skb, IFLA_MASTER, dev->master->ifindex); - txq = &dev->tx_queue; + txq = netdev_get_tx_queue(dev, 0); if (txq->qdisc_sleeping) NLA_PUT_STRING(skb, IFLA_QDISC, txq->qdisc_sleeping->ops->id); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index af0056e7e5b3..b486e634f4fe 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -621,7 +621,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) /* ensure that TX flow won't interrupt us * until the end of the call to requeue function */ - txq = &local->mdev->tx_queue; + txq = netdev_get_tx_queue(local->mdev, 0); spin_lock_bh(&txq->lock); /* create a new queue for this aggregation */ @@ -862,7 +862,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid) /* avoid ordering issues: we are the only one that can modify * the content of the qdiscs */ - txq = &local->mdev->tx_queue; + txq = netdev_get_tx_queue(local->mdev, 0); spin_lock_bh(&txq->lock); /* remove the queue for this aggregation */ ieee80211_ht_agg_queue_remove(local, sta, tid, 1); diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 6ae43a3c7726..f014cd38c2d0 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -574,7 +574,7 @@ static struct Qdisc_ops wme_qdisc_ops __read_mostly = void ieee80211_install_qdisc(struct net_device *dev) { - struct netdev_queue *txq = &dev->tx_queue; + struct netdev_queue *txq = netdev_get_tx_queue(dev, 0); struct Qdisc *qdisc; qdisc = qdisc_create_dflt(dev, txq, @@ -596,7 +596,7 @@ void ieee80211_install_qdisc(struct net_device *dev) int ieee80211_qdisc_installed(struct net_device *dev) { - struct netdev_queue *txq = &dev->tx_queue; + struct netdev_queue *txq = netdev_get_tx_queue(dev, 0); return txq->qdisc_sleeping->ops == &wme_qdisc_ops; } @@ -617,7 +617,7 @@ int ieee80211_ht_agg_queue_add(struct ieee80211_local *local, struct sta_info *sta, u16 tid) { int i; - struct netdev_queue *txq = &local->mdev->tx_queue; + struct netdev_queue *txq = netdev_get_tx_queue(local->mdev, 0); struct ieee80211_sched_data *q = qdisc_priv(txq->qdisc_sleeping); DECLARE_MAC_BUF(mac); @@ -652,14 +652,14 @@ int ieee80211_ht_agg_queue_add(struct ieee80211_local *local, } /** - * the caller needs to hold local->mdev->tx_queue.lock + * the caller needs to hold netdev_get_tx_queue(local->mdev, X)->lock */ void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local, struct sta_info *sta, u16 tid, u8 requeue) { struct ieee80211_hw *hw = &local->hw; - struct netdev_queue *txq = &local->mdev->tx_queue; + struct netdev_queue *txq = netdev_get_tx_queue(local->mdev, 0); struct ieee80211_sched_data *q = qdisc_priv(txq->qdisc_sleeping); int agg_queue = sta->tid_to_tx_q[tid]; @@ -676,7 +676,7 @@ void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local, void ieee80211_requeue(struct ieee80211_local *local, int queue) { - struct netdev_queue *txq = &local->mdev->tx_queue; + struct netdev_queue *txq = netdev_get_tx_queue(local->mdev, 0); struct Qdisc *root_qd = txq->qdisc_sleeping; struct ieee80211_sched_data *q = qdisc_priv(root_qd); struct Qdisc *qdisc = q->queues[queue]; diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index 819afc449e1e..d41be0d66eb0 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -74,14 +74,16 @@ static const struct proto_ops nr_proto_ops; */ static struct lock_class_key nr_netdev_xmit_lock_key; -static void nr_set_lockdep_one(struct netdev_queue *txq) +static void nr_set_lockdep_one(struct net_device *dev, + struct netdev_queue *txq, + void *_unused) { lockdep_set_class(&txq->_xmit_lock, &nr_netdev_xmit_lock_key); } static void nr_set_lockdep_key(struct net_device *dev) { - nr_set_lockdep_one(&dev->tx_queue); + netdev_for_each_tx_queue(dev, nr_set_lockdep_one, NULL); } /* diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index 7dbbc0891623..f3a691f34909 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -75,14 +75,16 @@ ax25_address rose_callsign; */ static struct lock_class_key rose_netdev_xmit_lock_key; -static void rose_set_lockdep_one(struct netdev_queue *txq) +static void rose_set_lockdep_one(struct net_device *dev, + struct netdev_queue *txq, + void *_unused) { lockdep_set_class(&txq->_xmit_lock, &rose_netdev_xmit_lock_key); } static void rose_set_lockdep_key(struct net_device *dev) { - rose_set_lockdep_one(&dev->tx_queue); + netdev_for_each_tx_queue(dev, rose_set_lockdep_one, NULL); } /* diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index b483bbea6118..d0b0a9b14394 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -166,7 +166,7 @@ replay: /* Find qdisc */ if (!parent) { - struct netdev_queue *dev_queue = &dev->tx_queue; + struct netdev_queue *dev_queue = netdev_get_tx_queue(dev, 0); q = dev_queue->qdisc_sleeping; parent = q->handle; } else { @@ -410,7 +410,7 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb) if ((dev = dev_get_by_index(&init_net, tcm->tcm_ifindex)) == NULL) return skb->len; - dev_queue = &dev->tx_queue; + dev_queue = netdev_get_tx_queue(dev, 0); if (!tcm->tcm_parent) q = dev_queue->qdisc_sleeping; else diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 95873f8dd37c..830ccc544a15 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -183,9 +183,8 @@ EXPORT_SYMBOL(unregister_qdisc); (root qdisc, all its children, children of children etc.) */ -struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle) +static struct Qdisc *__qdisc_lookup(struct netdev_queue *dev_queue, u32 handle) { - struct netdev_queue *dev_queue = &dev->tx_queue; struct Qdisc *q; list_for_each_entry(q, &dev_queue->qdisc_list, list) { @@ -195,6 +194,19 @@ struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle) return NULL; } +struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle) +{ + unsigned int i; + + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + struct Qdisc *q = __qdisc_lookup(txq, handle); + if (q) + return q; + } + return NULL; +} + static struct Qdisc *qdisc_leaf(struct Qdisc *p, u32 classid) { unsigned long cl; @@ -462,7 +474,7 @@ dev_graft_qdisc(struct net_device *dev, struct Qdisc *qdisc) } } else { - dev_queue = &dev->tx_queue; + dev_queue = netdev_get_tx_queue(dev, 0); oqdisc = dev_queue->qdisc_sleeping; /* Prune old scheduler */ @@ -742,7 +754,8 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg) q = dev->rx_queue.qdisc; } } else { - struct netdev_queue *dev_queue = &dev->tx_queue; + struct netdev_queue *dev_queue; + dev_queue = netdev_get_tx_queue(dev, 0); q = dev_queue->qdisc_sleeping; } if (!q) @@ -817,7 +830,8 @@ replay: q = dev->rx_queue.qdisc; } } else { - struct netdev_queue *dev_queue = &dev->tx_queue; + struct netdev_queue *dev_queue; + dev_queue = netdev_get_tx_queue(dev, 0); q = dev_queue->qdisc_sleeping; } @@ -899,7 +913,7 @@ create_n_graft: tcm->tcm_parent, tcm->tcm_parent, tca, &err); else - q = qdisc_create(dev, &dev->tx_queue, + q = qdisc_create(dev, netdev_get_tx_queue(dev, 0), tcm->tcm_parent, tcm->tcm_handle, tca, &err); if (q == NULL) { @@ -1025,7 +1039,7 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb) if (idx > s_idx) s_q_idx = 0; q_idx = 0; - dev_queue = &dev->tx_queue; + dev_queue = netdev_get_tx_queue(dev, 0); list_for_each_entry(q, &dev_queue->qdisc_list, list) { if (q_idx < s_q_idx) { q_idx++; @@ -1098,7 +1112,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg) /* Step 1. Determine qdisc handle X:0 */ - dev_queue = &dev->tx_queue; + dev_queue = netdev_get_tx_queue(dev, 0); if (pid != TC_H_ROOT) { u32 qid1 = TC_H_MAJ(pid); @@ -1275,7 +1289,7 @@ static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb) s_t = cb->args[0]; t = 0; - dev_queue = &dev->tx_queue; + dev_queue = netdev_get_tx_queue(dev, 0); list_for_each_entry(q, &dev_queue->qdisc_list, list) { if (t < s_t || !q->ops->cl_ops || (tcm->tcm_parent && diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 243de935b182..4e2b865cbba0 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -40,20 +40,30 @@ */ void qdisc_lock_tree(struct net_device *dev) - __acquires(dev->tx_queue.lock) __acquires(dev->rx_queue.lock) { - spin_lock_bh(&dev->tx_queue.lock); + unsigned int i; + + local_bh_disable(); + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + spin_lock(&txq->lock); + } spin_lock(&dev->rx_queue.lock); } EXPORT_SYMBOL(qdisc_lock_tree); void qdisc_unlock_tree(struct net_device *dev) __releases(dev->rx_queue.lock) - __releases(dev->tx_queue.lock) { + unsigned int i; + spin_unlock(&dev->rx_queue.lock); - spin_unlock_bh(&dev->tx_queue.lock); + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + spin_unlock(&txq->lock); + } + local_bh_enable(); } EXPORT_SYMBOL(qdisc_unlock_tree); @@ -212,22 +222,37 @@ void __qdisc_run(struct netdev_queue *txq) static void dev_watchdog(unsigned long arg) { struct net_device *dev = (struct net_device *)arg; - struct netdev_queue *txq = &dev->tx_queue; netif_tx_lock(dev); - if (txq->qdisc != &noop_qdisc) { + if (!qdisc_tx_is_noop(dev)) { if (netif_device_present(dev) && netif_running(dev) && netif_carrier_ok(dev)) { - if (netif_queue_stopped(dev) && - time_after(jiffies, dev->trans_start + dev->watchdog_timeo)) { + int some_queue_stopped = 0; + unsigned int i; + + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq; + + txq = netdev_get_tx_queue(dev, i); + if (netif_tx_queue_stopped(txq)) { + some_queue_stopped = 1; + break; + } + } - printk(KERN_INFO "NETDEV WATCHDOG: %s: transmit timed out\n", + if (some_queue_stopped && + time_after(jiffies, (dev->trans_start + + dev->watchdog_timeo))) { + printk(KERN_INFO "NETDEV WATCHDOG: %s: " + "transmit timed out\n", dev->name); dev->tx_timeout(dev); WARN_ON_ONCE(1); } - if (!mod_timer(&dev->watchdog_timer, round_jiffies(jiffies + dev->watchdog_timeo))) + if (!mod_timer(&dev->watchdog_timer, + round_jiffies(jiffies + + dev->watchdog_timeo))) dev_hold(dev); } } @@ -542,9 +567,55 @@ void qdisc_destroy(struct Qdisc *qdisc) } EXPORT_SYMBOL(qdisc_destroy); +static bool dev_all_qdisc_sleeping_noop(struct net_device *dev) +{ + unsigned int i; + + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + + if (txq->qdisc_sleeping != &noop_qdisc) + return false; + } + return true; +} + +static void attach_one_default_qdisc(struct net_device *dev, + struct netdev_queue *dev_queue, + void *_unused) +{ + struct Qdisc *qdisc; + + if (dev->tx_queue_len) { + qdisc = qdisc_create_dflt(dev, dev_queue, + &pfifo_fast_ops, TC_H_ROOT); + if (!qdisc) { + printk(KERN_INFO "%s: activation failed\n", dev->name); + return; + } + list_add_tail(&qdisc->list, &dev_queue->qdisc_list); + } else { + qdisc = &noqueue_qdisc; + } + dev_queue->qdisc_sleeping = qdisc; +} + +static void transition_one_qdisc(struct net_device *dev, + struct netdev_queue *dev_queue, + void *_need_watchdog) +{ + int *need_watchdog_p = _need_watchdog; + + spin_lock_bh(&dev_queue->lock); + rcu_assign_pointer(dev_queue->qdisc, dev_queue->qdisc_sleeping); + if (dev_queue->qdisc != &noqueue_qdisc) + *need_watchdog_p = 1; + spin_unlock_bh(&dev_queue->lock); +} + void dev_activate(struct net_device *dev) { - struct netdev_queue *txq = &dev->tx_queue; + int need_watchdog; /* No queueing discipline is attached to device; create default one i.e. pfifo_fast for devices, @@ -552,39 +623,27 @@ void dev_activate(struct net_device *dev) virtual interfaces */ - if (txq->qdisc_sleeping == &noop_qdisc) { - struct Qdisc *qdisc; - if (dev->tx_queue_len) { - qdisc = qdisc_create_dflt(dev, txq, - &pfifo_fast_ops, - TC_H_ROOT); - if (qdisc == NULL) { - printk(KERN_INFO "%s: activation failed\n", dev->name); - return; - } - list_add_tail(&qdisc->list, &txq->qdisc_list); - } else { - qdisc = &noqueue_qdisc; - } - txq->qdisc_sleeping = qdisc; - } + if (dev_all_qdisc_sleeping_noop(dev)) + netdev_for_each_tx_queue(dev, attach_one_default_qdisc, NULL); if (!netif_carrier_ok(dev)) /* Delay activation until next carrier-on event */ return; - spin_lock_bh(&txq->lock); - rcu_assign_pointer(txq->qdisc, txq->qdisc_sleeping); - if (txq->qdisc != &noqueue_qdisc) { + need_watchdog = 0; + netdev_for_each_tx_queue(dev, transition_one_qdisc, &need_watchdog); + + if (need_watchdog) { dev->trans_start = jiffies; dev_watchdog_up(dev); } - spin_unlock_bh(&txq->lock); } -static void dev_deactivate_queue(struct netdev_queue *dev_queue, - struct Qdisc *qdisc_default) +static void dev_deactivate_queue(struct net_device *dev, + struct netdev_queue *dev_queue, + void *_qdisc_default) { + struct Qdisc *qdisc_default = _qdisc_default; struct Qdisc *qdisc; struct sk_buff *skb; @@ -603,12 +662,35 @@ static void dev_deactivate_queue(struct netdev_queue *dev_queue, kfree_skb(skb); } +static bool some_qdisc_is_running(struct net_device *dev, int lock) +{ + unsigned int i; + + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *dev_queue; + int val; + + dev_queue = netdev_get_tx_queue(dev, i); + + if (lock) + spin_lock_bh(&dev_queue->lock); + + val = test_bit(__QUEUE_STATE_QDISC_RUNNING, &dev_queue->state); + + if (lock) + spin_unlock_bh(&dev_queue->lock); + + if (val) + return true; + } + return false; +} + void dev_deactivate(struct net_device *dev) { - struct netdev_queue *dev_queue = &dev->tx_queue; - int running; + bool running; - dev_deactivate_queue(dev_queue, &noop_qdisc); + netdev_for_each_tx_queue(dev, dev_deactivate_queue, &noop_qdisc); dev_watchdog_down(dev); @@ -617,17 +699,14 @@ void dev_deactivate(struct net_device *dev) /* Wait for outstanding qdisc_run calls. */ do { - while (test_bit(__QUEUE_STATE_QDISC_RUNNING, &dev_queue->state)) + while (some_qdisc_is_running(dev, 0)) yield(); /* * Double-check inside queue lock to ensure that all effects * of the queue run are visible when we return. */ - spin_lock_bh(&dev_queue->lock); - running = test_bit(__QUEUE_STATE_QDISC_RUNNING, - &dev_queue->state); - spin_unlock_bh(&dev_queue->lock); + running = some_qdisc_is_running(dev, 1); /* * The running flag should never be set at this point because @@ -642,8 +721,10 @@ void dev_deactivate(struct net_device *dev) static void dev_init_scheduler_queue(struct net_device *dev, struct netdev_queue *dev_queue, - struct Qdisc *qdisc) + void *_qdisc) { + struct Qdisc *qdisc = _qdisc; + dev_queue->qdisc = qdisc; dev_queue->qdisc_sleeping = qdisc; INIT_LIST_HEAD(&dev_queue->qdisc_list); @@ -652,18 +733,19 @@ static void dev_init_scheduler_queue(struct net_device *dev, void dev_init_scheduler(struct net_device *dev) { qdisc_lock_tree(dev); - dev_init_scheduler_queue(dev, &dev->tx_queue, &noop_qdisc); + netdev_for_each_tx_queue(dev, dev_init_scheduler_queue, &noop_qdisc); dev_init_scheduler_queue(dev, &dev->rx_queue, NULL); qdisc_unlock_tree(dev); setup_timer(&dev->watchdog_timer, dev_watchdog, (unsigned long)dev); } -static void dev_shutdown_scheduler_queue(struct net_device *dev, - struct netdev_queue *dev_queue, - struct Qdisc *qdisc_default) +static void shutdown_scheduler_queue(struct net_device *dev, + struct netdev_queue *dev_queue, + void *_qdisc_default) { struct Qdisc *qdisc = dev_queue->qdisc_sleeping; + struct Qdisc *qdisc_default = _qdisc_default; if (qdisc) { dev_queue->qdisc = qdisc_default; @@ -676,8 +758,8 @@ static void dev_shutdown_scheduler_queue(struct net_device *dev, void dev_shutdown(struct net_device *dev) { qdisc_lock_tree(dev); - dev_shutdown_scheduler_queue(dev, &dev->tx_queue, &noop_qdisc); - dev_shutdown_scheduler_queue(dev, &dev->rx_queue, NULL); + netdev_for_each_tx_queue(dev, shutdown_scheduler_queue, &noop_qdisc); + shutdown_scheduler_queue(dev, &dev->rx_queue, NULL); BUG_TRAP(!timer_pending(&dev->watchdog_timer)); qdisc_unlock_tree(dev); } diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index 8ac05981be20..44a2c3451f4d 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -111,7 +111,7 @@ teql_dequeue(struct Qdisc* sch) struct sk_buff *skb; skb = __skb_dequeue(&dat->q); - dat_queue = &dat->m->dev->tx_queue; + dat_queue = netdev_get_tx_queue(dat->m->dev, 0); if (skb == NULL) { struct net_device *m = qdisc_dev(dat_queue->qdisc); if (m) { @@ -155,10 +155,13 @@ teql_destroy(struct Qdisc* sch) if (q == master->slaves) { master->slaves = NEXT_SLAVE(q); if (q == master->slaves) { + struct netdev_queue *txq; + + txq = netdev_get_tx_queue(master->dev, 0); master->slaves = NULL; - spin_lock_bh(&master->dev->tx_queue.lock); - qdisc_reset(master->dev->tx_queue.qdisc); - spin_unlock_bh(&master->dev->tx_queue.lock); + spin_lock_bh(&txq->lock); + qdisc_reset(txq->qdisc); + spin_unlock_bh(&txq->lock); } } skb_queue_purge(&dat->q); @@ -218,7 +221,8 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt) static int __teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res, struct net_device *dev) { - struct teql_sched_data *q = qdisc_priv(dev->tx_queue.qdisc); + struct netdev_queue *dev_queue = netdev_get_tx_queue(dev, 0); + struct teql_sched_data *q = qdisc_priv(dev_queue->qdisc); struct neighbour *mn = skb->dst->neighbour; struct neighbour *n = q->ncache; @@ -254,7 +258,8 @@ __teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res, struct net_device * static inline int teql_resolve(struct sk_buff *skb, struct sk_buff *skb_res, struct net_device *dev) { - if (dev->tx_queue.qdisc == &noop_qdisc) + struct netdev_queue *txq = netdev_get_tx_queue(dev, 0); + if (txq->qdisc == &noop_qdisc) return -ENODEV; if (dev->header_ops == NULL || @@ -285,8 +290,10 @@ restart: do { struct net_device *slave = qdisc_dev(q); + struct netdev_queue *slave_txq; - if (slave->tx_queue.qdisc_sleeping != q) + slave_txq = netdev_get_tx_queue(slave, 0); + if (slave_txq->qdisc_sleeping != q) continue; if (netif_queue_stopped(slave) || __netif_subqueue_stopped(slave, subq) || -- cgit v1.2.3 From 09e83b5d7d1878065e2453239b49b684cd0fe4e5 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 17 Jul 2008 01:52:12 -0700 Subject: netdev: Kill NETIF_F_MULTI_QUEUE. There is no need for a feature bit for something that can be tested by simply checking the TX queue count. Signed-off-by: David S. Miller --- Documentation/networking/multiqueue.txt | 11 ----------- drivers/net/cpmac.c | 1 - drivers/net/igb/igb_main.c | 2 -- drivers/net/ixgbe/ixgbe_main.c | 2 -- drivers/net/s2io.c | 2 -- include/linux/netdevice.h | 4 +--- net/mac80211/main.c | 3 --- 7 files changed, 1 insertion(+), 24 deletions(-) (limited to 'include/linux') diff --git a/Documentation/networking/multiqueue.txt b/Documentation/networking/multiqueue.txt index e6dc1ee9e8f1..d391ea631141 100644 --- a/Documentation/networking/multiqueue.txt +++ b/Documentation/networking/multiqueue.txt @@ -24,15 +24,4 @@ netif_{start|stop|wake}_subqueue() functions to manage each queue while the device is still operational. netdev->queue_lock is still used when the device comes online or when it's completely shut down (unregister_netdev(), etc.). -Finally, the base driver should indicate that it is a multiqueue device. The -feature flag NETIF_F_MULTI_QUEUE should be added to the netdev->features -bitmap on device initialization. Below is an example from e1000: - -#ifdef CONFIG_E1000_MQ - if ( (adapter->hw.mac.type == e1000_82571) || - (adapter->hw.mac.type == e1000_82572) || - (adapter->hw.mac.type == e1000_80003es2lan)) - netdev->features |= NETIF_F_MULTI_QUEUE; -#endif - Author: Peter P. Waskiewicz Jr. diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c index d630e2a72f42..7c7b54e4828e 100644 --- a/drivers/net/cpmac.c +++ b/drivers/net/cpmac.c @@ -1165,7 +1165,6 @@ static int __devinit cpmac_probe(struct platform_device *pdev) dev->set_multicast_list = cpmac_set_multicast_list; dev->tx_timeout = cpmac_tx_timeout; dev->ethtool_ops = &cpmac_ethtool_ops; - dev->features |= NETIF_F_MULTI_QUEUE; netif_napi_add(dev, &priv->napi, cpmac_poll, 64); diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c index 64a150a16f39..471c194cd54e 100644 --- a/drivers/net/igb/igb_main.c +++ b/drivers/net/igb/igb_main.c @@ -1155,8 +1155,6 @@ static int __devinit igb_probe(struct pci_dev *pdev, if (pci_using_dac) netdev->features |= NETIF_F_HIGHDMA; - netdev->features |= NETIF_F_MULTI_QUEUE; - netdev->features |= NETIF_F_LLTX; adapter->en_mng_pt = igb_enable_mng_pass_thru(&adapter->hw); diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index 7d8bf94d3783..e6df9233f5ef 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -3566,8 +3566,6 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, if (pci_using_dac) netdev->features |= NETIF_F_HIGHDMA; - netdev->features |= NETIF_F_MULTI_QUEUE; - /* make sure the EEPROM is good */ if (ixgbe_validate_eeprom_checksum(hw, NULL) < 0) { dev_err(&pdev->dev, "The EEPROM Checksum Is Not Valid\n"); diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c index 517425dcb77c..5f0fcb04afff 100644 --- a/drivers/net/s2io.c +++ b/drivers/net/s2io.c @@ -7966,8 +7966,6 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre) dev->features |= NETIF_F_UFO; dev->features |= NETIF_F_HW_CSUM; } - if (config->multiq) - dev->features |= NETIF_F_MULTI_QUEUE; dev->tx_timeout = &s2io_tx_watchdog; dev->watchdog_timeo = WATCH_DOG_TIMEOUT; INIT_WORK(&sp->rst_timer_task, s2io_restart_nic); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index f25d4f5a31b0..c02227b9dd7b 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -533,7 +533,6 @@ struct net_device #define NETIF_F_LLTX 4096 /* LockLess TX - deprecated. Please */ /* do not use LLTX in new drivers */ #define NETIF_F_NETNS_LOCAL 8192 /* Does not change network namespaces */ -#define NETIF_F_MULTI_QUEUE 16384 /* Has multiple TX/RX queues */ #define NETIF_F_LRO 32768 /* large receive offload */ /* Segmentation offload features */ @@ -1163,11 +1162,10 @@ static inline void netif_wake_subqueue(struct net_device *dev, u16 queue_index) * @dev: network device * * Check if device has multiple transmit queues - * Always falls if NETDEVICE_MULTIQUEUE is not configured */ static inline int netif_is_multiqueue(const struct net_device *dev) { - return (!!(NETIF_F_MULTI_QUEUE & dev->features)); + return (dev->num_tx_queues > 1); } /* Use this variant when it is known for sure that it diff --git a/net/mac80211/main.c b/net/mac80211/main.c index b486e634f4fe..c74607eda1ee 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1678,9 +1678,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) if (!mdev) goto fail_mdev_alloc; - if (ieee80211_num_queues(hw) > 1) - mdev->features |= NETIF_F_MULTI_QUEUE; - mwdev = netdev_priv(mdev); mdev->ieee80211_ptr = mwdev; mwdev->wiphy = local->hw.wiphy; -- cgit v1.2.3 From 1d8ae3fdeb001b8f534a6782c261aba6ec1779f5 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 15 Jul 2008 02:52:19 -0700 Subject: pkt_sched: Remove RR scheduler. This actually fixes a bug added by the RR scheduler changes. The ->bands and ->prio2band parameters were being set outside of the sch_tree_lock() and thus could result in strange behavior and inconsistencies. It might be possible, in the new design (where there will be one qdisc per device TX queue) to allow similar functionality via a TX hash algorithm for RR but I really see no reason to export this aspect of how these multiqueue cards actually implement the scheduling of the the individual DMA TX rings and the single physical MAC/PHY port. Signed-off-by: David S. Miller --- include/linux/pkt_sched.h | 9 --- net/sched/sch_prio.c | 136 ++++++---------------------------------------- 2 files changed, 16 insertions(+), 129 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h index dbb7ac37960d..87f4e0fa8f27 100644 --- a/include/linux/pkt_sched.h +++ b/include/linux/pkt_sched.h @@ -103,15 +103,6 @@ struct tc_prio_qopt __u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> PRIO band */ }; -enum -{ - TCA_PRIO_UNSPEC, - TCA_PRIO_MQ, - __TCA_PRIO_MAX -}; - -#define TCA_PRIO_MAX (__TCA_PRIO_MAX - 1) - /* TBF section */ struct tc_tbf_qopt diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c index 39157f7bc046..536ca474dc69 100644 --- a/net/sched/sch_prio.c +++ b/net/sched/sch_prio.c @@ -24,11 +24,9 @@ struct prio_sched_data { int bands; - int curband; /* for round-robin */ struct tcf_proto *filter_list; u8 prio2band[TC_PRIO_MAX+1]; struct Qdisc *queues[TCQ_PRIO_BANDS]; - int mq; }; @@ -55,17 +53,14 @@ prio_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr) if (!q->filter_list || err < 0) { if (TC_H_MAJ(band)) band = 0; - band = q->prio2band[band&TC_PRIO_MAX]; - goto out; + return q->queues[q->prio2band[band&TC_PRIO_MAX]]; } band = res.classid; } band = TC_H_MIN(band) - 1; if (band >= q->bands) - band = q->prio2band[0]; -out: - if (q->mq) - skb_set_queue_mapping(skb, band); + return q->queues[q->prio2band[0]]; + return q->queues[band]; } @@ -123,68 +118,23 @@ prio_requeue(struct sk_buff *skb, struct Qdisc* sch) } -static struct sk_buff * -prio_dequeue(struct Qdisc* sch) +static struct sk_buff *prio_dequeue(struct Qdisc* sch) { - struct sk_buff *skb; struct prio_sched_data *q = qdisc_priv(sch); int prio; - struct Qdisc *qdisc; for (prio = 0; prio < q->bands; prio++) { - /* Check if the target subqueue is available before - * pulling an skb. This way we avoid excessive requeues - * for slower queues. - */ - if (!__netif_subqueue_stopped(qdisc_dev(sch), - (q->mq ? prio : 0))) { - qdisc = q->queues[prio]; - skb = qdisc->dequeue(qdisc); - if (skb) { - sch->q.qlen--; - return skb; - } + struct Qdisc *qdisc = q->queues[prio]; + struct sk_buff *skb = qdisc->dequeue(qdisc); + if (skb) { + sch->q.qlen--; + return skb; } } return NULL; } -static struct sk_buff *rr_dequeue(struct Qdisc* sch) -{ - struct sk_buff *skb; - struct prio_sched_data *q = qdisc_priv(sch); - struct Qdisc *qdisc; - int bandcount; - - /* Only take one pass through the queues. If nothing is available, - * return nothing. - */ - for (bandcount = 0; bandcount < q->bands; bandcount++) { - /* Check if the target subqueue is available before - * pulling an skb. This way we avoid excessive requeues - * for slower queues. If the queue is stopped, try the - * next queue. - */ - if (!__netif_subqueue_stopped(qdisc_dev(sch), - (q->mq ? q->curband : 0))) { - qdisc = q->queues[q->curband]; - skb = qdisc->dequeue(qdisc); - if (skb) { - sch->q.qlen--; - q->curband++; - if (q->curband >= q->bands) - q->curband = 0; - return skb; - } - } - q->curband++; - if (q->curband >= q->bands) - q->curband = 0; - } - return NULL; -} - static unsigned int prio_drop(struct Qdisc* sch) { struct prio_sched_data *q = qdisc_priv(sch); @@ -229,45 +179,22 @@ static int prio_tune(struct Qdisc *sch, struct nlattr *opt) { struct prio_sched_data *q = qdisc_priv(sch); struct tc_prio_qopt *qopt; - struct nlattr *tb[TCA_PRIO_MAX + 1]; - int err; int i; - err = nla_parse_nested_compat(tb, TCA_PRIO_MAX, opt, NULL, qopt, - sizeof(*qopt)); - if (err < 0) - return err; - - q->bands = qopt->bands; - /* If we're multiqueue, make sure the number of incoming bands - * matches the number of queues on the device we're associating with. - * If the number of bands requested is zero, then set q->bands to - * dev->egress_subqueue_count. Also, the root qdisc must be the - * only one that is enabled for multiqueue, since it's the only one - * that interacts with the underlying device. - */ - q->mq = nla_get_flag(tb[TCA_PRIO_MQ]); - if (q->mq) { - if (sch->parent != TC_H_ROOT) - return -EINVAL; - if (netif_is_multiqueue(qdisc_dev(sch))) { - if (q->bands == 0) - q->bands = qdisc_dev(sch)->egress_subqueue_count; - else if (q->bands != qdisc_dev(sch)->egress_subqueue_count) - return -EINVAL; - } else - return -EOPNOTSUPP; - } + if (nla_len(opt) < sizeof(*qopt)) + return -EINVAL; + qopt = nla_data(opt); - if (q->bands > TCQ_PRIO_BANDS || q->bands < 2) + if (qopt->bands > TCQ_PRIO_BANDS || qopt->bands < 2) return -EINVAL; for (i=0; i<=TC_PRIO_MAX; i++) { - if (qopt->priomap[i] >= q->bands) + if (qopt->priomap[i] >= qopt->bands) return -EINVAL; } sch_tree_lock(sch); + q->bands = qopt->bands; memcpy(q->prio2band, qopt->priomap, TC_PRIO_MAX+1); for (i=q->bands; imq) { - if (nla_put_flag(skb, TCA_PRIO_MQ) < 0) - goto nla_put_failure; - } nla_nest_compat_end(skb, nest); return skb->len; @@ -509,44 +432,17 @@ static struct Qdisc_ops prio_qdisc_ops __read_mostly = { .owner = THIS_MODULE, }; -static struct Qdisc_ops rr_qdisc_ops __read_mostly = { - .next = NULL, - .cl_ops = &prio_class_ops, - .id = "rr", - .priv_size = sizeof(struct prio_sched_data), - .enqueue = prio_enqueue, - .dequeue = rr_dequeue, - .requeue = prio_requeue, - .drop = prio_drop, - .init = prio_init, - .reset = prio_reset, - .destroy = prio_destroy, - .change = prio_tune, - .dump = prio_dump, - .owner = THIS_MODULE, -}; - static int __init prio_module_init(void) { - int err; - - err = register_qdisc(&prio_qdisc_ops); - if (err < 0) - return err; - err = register_qdisc(&rr_qdisc_ops); - if (err < 0) - unregister_qdisc(&prio_qdisc_ops); - return err; + return register_qdisc(&prio_qdisc_ops); } static void __exit prio_module_exit(void) { unregister_qdisc(&prio_qdisc_ops); - unregister_qdisc(&rr_qdisc_ops); } module_init(prio_module_init) module_exit(prio_module_exit) MODULE_LICENSE("GPL"); -MODULE_ALIAS("sch_rr"); -- cgit v1.2.3 From fd2ea0a79faad824258af5dcec1927aa24d81c16 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 17 Jul 2008 01:56:23 -0700 Subject: net: Use queue aware tests throughout. This effectively "flips the switch" by making the core networking and multiqueue-aware drivers use the new TX multiqueue structures. Non-multiqueue drivers need no changes. The interfaces they use such as netif_stop_queue() degenerate into an operation on TX queue zero. So everything "just works" for them. Code that really wants to do "X" to all TX queues now invokes a routine that does so, such as netif_tx_wake_all_queues(), netif_tx_stop_all_queues(), etc. pktgen and netpoll required a little bit more surgery than the others. In particular the pktgen changes, whilst functional, could be largely improved. The initial check in pktgen_xmit() will sometimes check the wrong queue, which is mostly harmless. The thing to do is probably to invoke fill_packet() earlier. The bulk of the netpoll changes is to make the code operate solely on the TX queue indicated by by the SKB queue mapping. Setting of the SKB queue mapping is entirely confined inside of net/core/dev.c:dev_pick_tx(). If we end up needing any kind of special semantics (drops, for example) it will be implemented here. Finally, we now have a "real_num_tx_queues" which is where the driver indicates how many TX queues are actually active. With IGB changes from Jeff Kirsher. Signed-off-by: David S. Miller --- drivers/net/cpmac.c | 20 ++++------ drivers/net/igb/igb_main.c | 19 +++------ drivers/net/ixgbe/ixgbe_ethtool.c | 10 +---- drivers/net/ixgbe/ixgbe_main.c | 15 +++---- drivers/net/s2io.c | 48 +++++++++-------------- include/linux/netdevice.h | 82 ++++++++++++++++++++++++++++++++++----- include/net/pkt_sched.h | 4 +- net/core/dev.c | 28 ++++++------- net/core/netpoll.c | 24 +++++++----- net/core/pktgen.c | 69 ++++++++++++++++++++------------ net/sched/sch_generic.c | 5 +-- net/sched/sch_teql.c | 6 +-- 12 files changed, 187 insertions(+), 143 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c index 7c7b54e4828e..fbd4280c102c 100644 --- a/drivers/net/cpmac.c +++ b/drivers/net/cpmac.c @@ -544,7 +544,7 @@ fatal_error: spin_unlock(&priv->rx_lock); netif_rx_complete(priv->dev, napi); - netif_stop_queue(priv->dev); + netif_tx_stop_all_queues(priv->dev); napi_disable(&priv->napi); atomic_inc(&priv->reset_pending); @@ -750,9 +750,7 @@ static void cpmac_hw_error(struct work_struct *work) barrier(); atomic_dec(&priv->reset_pending); - for (i = 0; i < CPMAC_QUEUES; i++) - netif_wake_subqueue(priv->dev, i); - netif_wake_queue(priv->dev); + netif_tx_wake_all_queues(priv->dev); cpmac_write(priv->regs, CPMAC_MAC_INT_ENABLE, 3); } @@ -781,7 +779,7 @@ static void cpmac_check_status(struct net_device *dev) dev->name, tx_code, tx_channel, macstatus); } - netif_stop_queue(dev); + netif_tx_stop_all_queues(dev); cpmac_hw_stop(dev); if (schedule_work(&priv->reset_work)) atomic_inc(&priv->reset_pending); @@ -842,9 +840,7 @@ static void cpmac_tx_timeout(struct net_device *dev) barrier(); atomic_dec(&priv->reset_pending); - netif_wake_queue(priv->dev); - for (i = 0; i < CPMAC_QUEUES; i++) - netif_wake_subqueue(dev, i); + netif_tx_wake_all_queues(priv->dev); } static int cpmac_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) @@ -935,7 +931,7 @@ static void cpmac_adjust_link(struct net_device *dev) spin_lock(&priv->lock); if (priv->phy->link) { - netif_start_queue(dev); + netif_tx_start_all_queues(dev); if (priv->phy->duplex != priv->oldduplex) { new_state = 1; priv->oldduplex = priv->phy->duplex; @@ -949,10 +945,10 @@ static void cpmac_adjust_link(struct net_device *dev) if (!priv->oldlink) { new_state = 1; priv->oldlink = 1; - netif_schedule(dev); + netif_tx_schedule_all(dev); } } else if (priv->oldlink) { - netif_stop_queue(dev); + netif_tx_stop_all_queues(dev); new_state = 1; priv->oldlink = 0; priv->oldspeed = 0; @@ -1072,7 +1068,7 @@ static int cpmac_stop(struct net_device *dev) struct cpmac_priv *priv = netdev_priv(dev); struct resource *mem; - netif_stop_queue(dev); + netif_tx_stop_all_queues(dev); cancel_work_sync(&priv->reset_work); napi_disable(&priv->napi); diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c index 471c194cd54e..81bba6983dde 100644 --- a/drivers/net/igb/igb_main.c +++ b/drivers/net/igb/igb_main.c @@ -533,7 +533,7 @@ msi_only: adapter->flags |= IGB_FLAG_HAS_MSI; /* Notify the stack of the (possibly) reduced Tx Queue count. */ - adapter->netdev->egress_subqueue_count = adapter->num_tx_queues; + adapter->netdev->real_num_tx_queues = adapter->num_tx_queues; return; } @@ -821,9 +821,7 @@ void igb_down(struct igb_adapter *adapter) wr32(E1000_RCTL, rctl & ~E1000_RCTL_EN); /* flush and sleep below */ - netif_stop_queue(netdev); - for (i = 0; i < adapter->num_tx_queues; i++) - netif_stop_subqueue(netdev, i); + netif_tx_stop_all_queues(netdev); /* disable transmits in the hardware */ tctl = rd32(E1000_TCTL); @@ -1266,9 +1264,7 @@ static int __devinit igb_probe(struct pci_dev *pdev, /* tell the stack to leave us alone until igb_open() is called */ netif_carrier_off(netdev); - netif_stop_queue(netdev); - for (i = 0; i < adapter->num_tx_queues; i++) - netif_stop_subqueue(netdev, i); + netif_tx_stop_all_queues(netdev); strcpy(netdev->name, "eth%d"); err = register_netdev(netdev); @@ -2315,7 +2311,6 @@ static void igb_watchdog_task(struct work_struct *work) struct e1000_mac_info *mac = &adapter->hw.mac; u32 link; s32 ret_val; - int i; if ((netif_carrier_ok(netdev)) && (rd32(E1000_STATUS) & E1000_STATUS_LU)) @@ -2371,9 +2366,7 @@ static void igb_watchdog_task(struct work_struct *work) } netif_carrier_on(netdev); - netif_wake_queue(netdev); - for (i = 0; i < adapter->num_tx_queues; i++) - netif_wake_subqueue(netdev, i); + netif_tx_wake_all_queues(netdev); if (!test_bit(__IGB_DOWN, &adapter->state)) mod_timer(&adapter->phy_info_timer, @@ -2385,9 +2378,7 @@ static void igb_watchdog_task(struct work_struct *work) adapter->link_duplex = 0; dev_info(&adapter->pdev->dev, "NIC Link is Down\n"); netif_carrier_off(netdev); - netif_stop_queue(netdev); - for (i = 0; i < adapter->num_tx_queues; i++) - netif_stop_subqueue(netdev, i); + netif_tx_stop_all_queues(netdev); if (!test_bit(__IGB_DOWN, &adapter->state)) mod_timer(&adapter->phy_info_timer, round_jiffies(jiffies + 2 * HZ)); diff --git a/drivers/net/ixgbe/ixgbe_ethtool.c b/drivers/net/ixgbe/ixgbe_ethtool.c index 81b769093d22..3efe5dda10af 100644 --- a/drivers/net/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ixgbe/ixgbe_ethtool.c @@ -252,16 +252,10 @@ static int ixgbe_set_tso(struct net_device *netdev, u32 data) netdev->features |= NETIF_F_TSO; netdev->features |= NETIF_F_TSO6; } else { - struct ixgbe_adapter *adapter = netdev_priv(netdev); - int i; - netif_stop_queue(netdev); - for (i = 0; i < adapter->num_tx_queues; i++) - netif_stop_subqueue(netdev, i); + netif_tx_stop_all_queues(netdev); netdev->features &= ~NETIF_F_TSO; netdev->features &= ~NETIF_F_TSO6; - for (i = 0; i < adapter->num_tx_queues; i++) - netif_start_subqueue(netdev, i); - netif_start_queue(netdev); + netif_tx_start_all_queues(netdev); } return 0; } diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index e6df9233f5ef..6af8fb5c4b5f 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -2013,7 +2013,7 @@ void ixgbe_down(struct ixgbe_adapter *adapter) del_timer_sync(&adapter->watchdog_timer); netif_carrier_off(netdev); - netif_stop_queue(netdev); + netif_tx_stop_all_queues(netdev); if (!pci_channel_offline(adapter->pdev)) ixgbe_reset(adapter); @@ -2359,7 +2359,7 @@ try_msi: out: /* Notify the stack of the (possibly) reduced Tx Queue count. */ - adapter->netdev->egress_subqueue_count = adapter->num_tx_queues; + adapter->netdev->real_num_tx_queues = adapter->num_tx_queues; return err; } @@ -2896,7 +2896,6 @@ static void ixgbe_watchdog(unsigned long data) struct net_device *netdev = adapter->netdev; bool link_up; u32 link_speed = 0; - int i; adapter->hw.mac.ops.check_link(&adapter->hw, &(link_speed), &link_up); @@ -2917,9 +2916,7 @@ static void ixgbe_watchdog(unsigned long data) (FLOW_TX ? "TX" : "None")))); netif_carrier_on(netdev); - netif_wake_queue(netdev); - for (i = 0; i < adapter->num_tx_queues; i++) - netif_wake_subqueue(netdev, i); + netif_tx_wake_all_queues(netdev); } else { /* Force detection of hung controller */ adapter->detect_tx_hung = true; @@ -2928,7 +2925,7 @@ static void ixgbe_watchdog(unsigned long data) if (netif_carrier_ok(netdev)) { DPRINTK(LINK, INFO, "NIC Link is Down\n"); netif_carrier_off(netdev); - netif_stop_queue(netdev); + netif_tx_stop_all_queues(netdev); } } @@ -3631,9 +3628,7 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, ixgbe_start_hw(hw); netif_carrier_off(netdev); - netif_stop_queue(netdev); - for (i = 0; i < adapter->num_tx_queues; i++) - netif_stop_subqueue(netdev, i); + netif_tx_stop_all_queues(netdev); ixgbe_napi_add_all(adapter); diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c index 5f0fcb04afff..9dae40ccf048 100644 --- a/drivers/net/s2io.c +++ b/drivers/net/s2io.c @@ -545,63 +545,53 @@ static struct pci_driver s2io_driver = { /* netqueue manipulation helper functions */ static inline void s2io_stop_all_tx_queue(struct s2io_nic *sp) { - int i; - if (sp->config.multiq) { - for (i = 0; i < sp->config.tx_fifo_num; i++) - netif_stop_subqueue(sp->dev, i); - } else { + if (!sp->config.multiq) { + int i; + for (i = 0; i < sp->config.tx_fifo_num; i++) sp->mac_control.fifos[i].queue_state = FIFO_QUEUE_STOP; - netif_stop_queue(sp->dev); } + netif_tx_stop_all_queues(sp->dev); } static inline void s2io_stop_tx_queue(struct s2io_nic *sp, int fifo_no) { - if (sp->config.multiq) - netif_stop_subqueue(sp->dev, fifo_no); - else { + if (!sp->config.multiq) sp->mac_control.fifos[fifo_no].queue_state = FIFO_QUEUE_STOP; - netif_stop_queue(sp->dev); - } + + netif_tx_stop_all_queues(sp->dev); } static inline void s2io_start_all_tx_queue(struct s2io_nic *sp) { - int i; - if (sp->config.multiq) { - for (i = 0; i < sp->config.tx_fifo_num; i++) - netif_start_subqueue(sp->dev, i); - } else { + if (!sp->config.multiq) { + int i; + for (i = 0; i < sp->config.tx_fifo_num; i++) sp->mac_control.fifos[i].queue_state = FIFO_QUEUE_START; - netif_start_queue(sp->dev); } + netif_tx_start_all_queues(sp->dev); } static inline void s2io_start_tx_queue(struct s2io_nic *sp, int fifo_no) { - if (sp->config.multiq) - netif_start_subqueue(sp->dev, fifo_no); - else { + if (!sp->config.multiq) sp->mac_control.fifos[fifo_no].queue_state = FIFO_QUEUE_START; - netif_start_queue(sp->dev); - } + + netif_tx_start_all_queues(sp->dev); } static inline void s2io_wake_all_tx_queue(struct s2io_nic *sp) { - int i; - if (sp->config.multiq) { - for (i = 0; i < sp->config.tx_fifo_num; i++) - netif_wake_subqueue(sp->dev, i); - } else { + if (!sp->config.multiq) { + int i; + for (i = 0; i < sp->config.tx_fifo_num; i++) sp->mac_control.fifos[i].queue_state = FIFO_QUEUE_START; - netif_wake_queue(sp->dev); } + netif_tx_wake_all_queues(sp->dev); } static inline void s2io_wake_tx_queue( @@ -8691,5 +8681,5 @@ static void s2io_io_resume(struct pci_dev *pdev) } netif_device_attach(netdev); - netif_wake_queue(netdev); + netif_tx_wake_all_queues(netdev); } diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index c02227b9dd7b..b5c1e7df64fc 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -642,7 +642,13 @@ struct net_device struct netdev_queue rx_queue; struct netdev_queue *_tx ____cacheline_aligned_in_smp; + + /* Number of TX queues allocated at alloc_netdev_mq() time */ unsigned int num_tx_queues; + + /* Number of TX queues currently active in device */ + unsigned int real_num_tx_queues; + unsigned long tx_queue_len; /* Max frames per queue allowed */ /* @@ -1000,6 +1006,14 @@ static inline void netif_schedule(struct net_device *dev) netif_schedule_queue(netdev_get_tx_queue(dev, 0)); } +static inline void netif_tx_schedule_all(struct net_device *dev) +{ + unsigned int i; + + for (i = 0; i < dev->num_tx_queues; i++) + netif_schedule_queue(netdev_get_tx_queue(dev, i)); +} + /** * netif_start_queue - allow transmit * @dev: network device @@ -1016,6 +1030,16 @@ static inline void netif_start_queue(struct net_device *dev) netif_tx_start_queue(netdev_get_tx_queue(dev, 0)); } +static inline void netif_tx_start_all_queues(struct net_device *dev) +{ + unsigned int i; + + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + netif_tx_start_queue(txq); + } +} + /** * netif_wake_queue - restart transmit * @dev: network device @@ -1040,6 +1064,16 @@ static inline void netif_wake_queue(struct net_device *dev) netif_tx_wake_queue(netdev_get_tx_queue(dev, 0)); } +static inline void netif_tx_wake_all_queues(struct net_device *dev) +{ + unsigned int i; + + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + netif_tx_wake_queue(txq); + } +} + /** * netif_stop_queue - stop transmitted packets * @dev: network device @@ -1057,6 +1091,16 @@ static inline void netif_stop_queue(struct net_device *dev) netif_tx_stop_queue(netdev_get_tx_queue(dev, 0)); } +static inline void netif_tx_stop_all_queues(struct net_device *dev) +{ + unsigned int i; + + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + netif_tx_stop_queue(txq); + } +} + /** * netif_queue_stopped - test if transmit queue is flowblocked * @dev: network device @@ -1100,7 +1144,8 @@ static inline int netif_running(const struct net_device *dev) */ static inline void netif_start_subqueue(struct net_device *dev, u16 queue_index) { - clear_bit(__QUEUE_STATE_XOFF, &dev->egress_subqueue[queue_index].state); + struct netdev_queue *txq = netdev_get_tx_queue(dev, queue_index); + clear_bit(__QUEUE_STATE_XOFF, &txq->state); } /** @@ -1112,11 +1157,12 @@ static inline void netif_start_subqueue(struct net_device *dev, u16 queue_index) */ static inline void netif_stop_subqueue(struct net_device *dev, u16 queue_index) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, queue_index); #ifdef CONFIG_NETPOLL_TRAP if (netpoll_trap()) return; #endif - set_bit(__QUEUE_STATE_XOFF, &dev->egress_subqueue[queue_index].state); + set_bit(__QUEUE_STATE_XOFF, &txq->state); } /** @@ -1129,8 +1175,8 @@ static inline void netif_stop_subqueue(struct net_device *dev, u16 queue_index) static inline int __netif_subqueue_stopped(const struct net_device *dev, u16 queue_index) { - return test_bit(__QUEUE_STATE_XOFF, - &dev->egress_subqueue[queue_index].state); + struct netdev_queue *txq = netdev_get_tx_queue(dev, queue_index); + return test_bit(__QUEUE_STATE_XOFF, &txq->state); } static inline int netif_subqueue_stopped(const struct net_device *dev, @@ -1148,13 +1194,13 @@ static inline int netif_subqueue_stopped(const struct net_device *dev, */ static inline void netif_wake_subqueue(struct net_device *dev, u16 queue_index) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, queue_index); #ifdef CONFIG_NETPOLL_TRAP if (netpoll_trap()) return; #endif - if (test_and_clear_bit(__QUEUE_STATE_XOFF, - &dev->egress_subqueue[queue_index].state)) - __netif_schedule(netdev_get_tx_queue(dev, 0)); + if (test_and_clear_bit(__QUEUE_STATE_XOFF, &txq->state)) + __netif_schedule(txq); } /** @@ -1198,7 +1244,8 @@ extern int dev_set_mtu(struct net_device *, int); extern int dev_set_mac_address(struct net_device *, struct sockaddr *); extern int dev_hard_start_xmit(struct sk_buff *skb, - struct net_device *dev); + struct net_device *dev, + struct netdev_queue *txq); extern int netdev_budget; @@ -1447,6 +1494,12 @@ static inline void __netif_tx_lock(struct netdev_queue *txq, int cpu) txq->xmit_lock_owner = cpu; } +static inline void __netif_tx_lock_bh(struct netdev_queue *txq) +{ + spin_lock_bh(&txq->_xmit_lock); + txq->xmit_lock_owner = smp_processor_id(); +} + static inline void netif_tx_lock(struct net_device *dev) { int cpu = smp_processor_id(); @@ -1483,6 +1536,12 @@ static inline void __netif_tx_unlock(struct netdev_queue *txq) spin_unlock(&txq->_xmit_lock); } +static inline void __netif_tx_unlock_bh(struct netdev_queue *txq) +{ + txq->xmit_lock_owner = -1; + spin_unlock_bh(&txq->_xmit_lock); +} + static inline void netif_tx_unlock(struct net_device *dev) { unsigned int i; @@ -1514,8 +1573,13 @@ static inline void netif_tx_unlock_bh(struct net_device *dev) static inline void netif_tx_disable(struct net_device *dev) { + unsigned int i; + netif_tx_lock_bh(dev); - netif_stop_queue(dev); + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + netif_tx_stop_queue(txq); + } netif_tx_unlock_bh(dev); } diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h index d58c1a5eb845..cb9527815606 100644 --- a/include/net/pkt_sched.h +++ b/include/net/pkt_sched.h @@ -88,9 +88,7 @@ extern void __qdisc_run(struct netdev_queue *txq); static inline void qdisc_run(struct netdev_queue *txq) { - struct net_device *dev = txq->dev; - - if (!netif_queue_stopped(dev) && + if (!netif_tx_queue_stopped(txq) && !test_and_set_bit(__QUEUE_STATE_QDISC_RUNNING, &txq->state)) __qdisc_run(txq); } diff --git a/net/core/dev.c b/net/core/dev.c index 69378f250695..f027a1ac4fbb 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1598,7 +1598,8 @@ static int dev_gso_segment(struct sk_buff *skb) return 0; } -int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, + struct netdev_queue *txq) { if (likely(!skb->next)) { if (!list_empty(&ptype_all)) @@ -1627,9 +1628,7 @@ gso: skb->next = nskb; return rc; } - if (unlikely((netif_queue_stopped(dev) || - netif_subqueue_stopped(dev, skb)) && - skb->next)) + if (unlikely(netif_tx_queue_stopped(txq) && skb->next)) return NETDEV_TX_BUSY; } while (skb->next); @@ -1669,7 +1668,10 @@ out_kfree_skb: static struct netdev_queue *dev_pick_tx(struct net_device *dev, struct sk_buff *skb) { - return netdev_get_tx_queue(dev, 0); + u16 queue_index = 0; + + skb_set_queue_mapping(skb, queue_index); + return netdev_get_tx_queue(dev, queue_index); } int dev_queue_xmit(struct sk_buff *skb) @@ -1737,8 +1739,6 @@ gso: spin_lock(&txq->lock); q = txq->qdisc; if (q->enqueue) { - /* reset queue_mapping to zero */ - skb_set_queue_mapping(skb, 0); rc = q->enqueue(skb, q); qdisc_run(txq); spin_unlock(&txq->lock); @@ -1768,10 +1768,9 @@ gso: HARD_TX_LOCK(dev, txq, cpu); - if (!netif_queue_stopped(dev) && - !netif_subqueue_stopped(dev, skb)) { + if (!netif_tx_queue_stopped(txq)) { rc = 0; - if (!dev_hard_start_xmit(skb, dev)) { + if (!dev_hard_start_xmit(skb, dev, txq)) { HARD_TX_UNLOCK(dev, txq); goto out; } @@ -4160,8 +4159,7 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, BUG_ON(strlen(name) >= sizeof(dev->name)); - alloc_size = sizeof(struct net_device) + - sizeof(struct net_device_subqueue) * (queue_count - 1); + alloc_size = sizeof(struct net_device); if (sizeof_priv) { /* ensure 32-byte alignment of private area */ alloc_size = (alloc_size + NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST; @@ -4191,16 +4189,14 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, dev->_tx = tx; dev->num_tx_queues = queue_count; + dev->real_num_tx_queues = queue_count; if (sizeof_priv) { dev->priv = ((char *)dev + - ((sizeof(struct net_device) + - (sizeof(struct net_device_subqueue) * - (queue_count - 1)) + NETDEV_ALIGN_CONST) + ((sizeof(struct net_device) + NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST)); } - dev->egress_subqueue_count = queue_count; dev->gso_max_size = GSO_MAX_SIZE; netdev_init_queues(dev); diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 8fb134da0346..c12720895ecf 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -58,25 +58,27 @@ static void queue_process(struct work_struct *work) while ((skb = skb_dequeue(&npinfo->txq))) { struct net_device *dev = skb->dev; + struct netdev_queue *txq; if (!netif_device_present(dev) || !netif_running(dev)) { __kfree_skb(skb); continue; } + txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb)); + local_irq_save(flags); - netif_tx_lock(dev); - if ((netif_queue_stopped(dev) || - netif_subqueue_stopped(dev, skb)) || - dev->hard_start_xmit(skb, dev) != NETDEV_TX_OK) { + __netif_tx_lock(txq, smp_processor_id()); + if (netif_tx_queue_stopped(txq) || + dev->hard_start_xmit(skb, dev) != NETDEV_TX_OK) { skb_queue_head(&npinfo->txq, skb); - netif_tx_unlock(dev); + __netif_tx_unlock(txq); local_irq_restore(flags); schedule_delayed_work(&npinfo->tx_work, HZ/10); return; } - netif_tx_unlock(dev); + __netif_tx_unlock(txq); local_irq_restore(flags); } } @@ -278,17 +280,19 @@ static void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb) /* don't get messages out of order, and no recursion */ if (skb_queue_len(&npinfo->txq) == 0 && !netpoll_owner_active(dev)) { + struct netdev_queue *txq; unsigned long flags; + txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb)); + local_irq_save(flags); /* try until next clock tick */ for (tries = jiffies_to_usecs(1)/USEC_PER_POLL; tries > 0; --tries) { - if (netif_tx_trylock(dev)) { - if (!netif_queue_stopped(dev) && - !netif_subqueue_stopped(dev, skb)) + if (__netif_tx_trylock(txq)) { + if (!netif_tx_queue_stopped(txq)) status = dev->hard_start_xmit(skb, dev); - netif_tx_unlock(dev); + __netif_tx_unlock(txq); if (status == NETDEV_TX_OK) break; diff --git a/net/core/pktgen.c b/net/core/pktgen.c index fdf537707e51..906802db4ed4 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -2123,6 +2123,24 @@ static void get_ipsec_sa(struct pktgen_dev *pkt_dev, int flow) } } #endif +static void set_cur_queue_map(struct pktgen_dev *pkt_dev) +{ + if (pkt_dev->queue_map_min < pkt_dev->queue_map_max) { + __u16 t; + if (pkt_dev->flags & F_QUEUE_MAP_RND) { + t = random32() % + (pkt_dev->queue_map_max - + pkt_dev->queue_map_min + 1) + + pkt_dev->queue_map_min; + } else { + t = pkt_dev->cur_queue_map + 1; + if (t > pkt_dev->queue_map_max) + t = pkt_dev->queue_map_min; + } + pkt_dev->cur_queue_map = t; + } +} + /* Increment/randomize headers according to flags and current values * for IP src/dest, UDP src/dst port, MAC-Addr src/dst */ @@ -2325,19 +2343,7 @@ static void mod_cur_headers(struct pktgen_dev *pkt_dev) pkt_dev->cur_pkt_size = t; } - if (pkt_dev->queue_map_min < pkt_dev->queue_map_max) { - __u16 t; - if (pkt_dev->flags & F_QUEUE_MAP_RND) { - t = random32() % - (pkt_dev->queue_map_max - pkt_dev->queue_map_min + 1) - + pkt_dev->queue_map_min; - } else { - t = pkt_dev->cur_queue_map + 1; - if (t > pkt_dev->queue_map_max) - t = pkt_dev->queue_map_min; - } - pkt_dev->cur_queue_map = t; - } + set_cur_queue_map(pkt_dev); pkt_dev->flows[flow].count++; } @@ -2458,7 +2464,7 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev, __be16 *vlan_encapsulated_proto = NULL; /* packet type ID field (or len) for VLAN tag */ __be16 *svlan_tci = NULL; /* Encapsulates priority and SVLAN ID */ __be16 *svlan_encapsulated_proto = NULL; /* packet type ID field (or len) for SVLAN tag */ - + u16 queue_map; if (pkt_dev->nr_labels) protocol = htons(ETH_P_MPLS_UC); @@ -2469,6 +2475,7 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev, /* Update any of the values, used when we're incrementing various * fields. */ + queue_map = pkt_dev->cur_queue_map; mod_cur_headers(pkt_dev); datalen = (odev->hard_header_len + 16) & ~0xf; @@ -2507,7 +2514,7 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev, skb->network_header = skb->tail; skb->transport_header = skb->network_header + sizeof(struct iphdr); skb_put(skb, sizeof(struct iphdr) + sizeof(struct udphdr)); - skb_set_queue_mapping(skb, pkt_dev->cur_queue_map); + skb_set_queue_mapping(skb, queue_map); iph = ip_hdr(skb); udph = udp_hdr(skb); @@ -2797,6 +2804,7 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev, __be16 *vlan_encapsulated_proto = NULL; /* packet type ID field (or len) for VLAN tag */ __be16 *svlan_tci = NULL; /* Encapsulates priority and SVLAN ID */ __be16 *svlan_encapsulated_proto = NULL; /* packet type ID field (or len) for SVLAN tag */ + u16 queue_map; if (pkt_dev->nr_labels) protocol = htons(ETH_P_MPLS_UC); @@ -2807,6 +2815,7 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev, /* Update any of the values, used when we're incrementing various * fields. */ + queue_map = pkt_dev->cur_queue_map; mod_cur_headers(pkt_dev); skb = alloc_skb(pkt_dev->cur_pkt_size + 64 + 16 + @@ -2844,7 +2853,7 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev, skb->network_header = skb->tail; skb->transport_header = skb->network_header + sizeof(struct ipv6hdr); skb_put(skb, sizeof(struct ipv6hdr) + sizeof(struct udphdr)); - skb_set_queue_mapping(skb, pkt_dev->cur_queue_map); + skb_set_queue_mapping(skb, queue_map); iph = ipv6_hdr(skb); udph = udp_hdr(skb); @@ -3263,7 +3272,9 @@ static void pktgen_rem_thread(struct pktgen_thread *t) static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev) { struct net_device *odev = NULL; + struct netdev_queue *txq; __u64 idle_start = 0; + u16 queue_map; int ret; odev = pkt_dev->odev; @@ -3285,9 +3296,15 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev) } } - if ((netif_queue_stopped(odev) || - (pkt_dev->skb && - netif_subqueue_stopped(odev, pkt_dev->skb))) || + if (!pkt_dev->skb) { + set_cur_queue_map(pkt_dev); + queue_map = pkt_dev->cur_queue_map; + } else { + queue_map = skb_get_queue_mapping(pkt_dev->skb); + } + + txq = netdev_get_tx_queue(odev, queue_map); + if (netif_tx_queue_stopped(txq) || need_resched()) { idle_start = getCurUs(); @@ -3303,8 +3320,7 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev) pkt_dev->idle_acc += getCurUs() - idle_start; - if (netif_queue_stopped(odev) || - netif_subqueue_stopped(odev, pkt_dev->skb)) { + if (netif_tx_queue_stopped(txq)) { pkt_dev->next_tx_us = getCurUs(); /* TODO */ pkt_dev->next_tx_ns = 0; goto out; /* Try the next interface */ @@ -3331,9 +3347,12 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev) } } - netif_tx_lock_bh(odev); - if (!netif_queue_stopped(odev) && - !netif_subqueue_stopped(odev, pkt_dev->skb)) { + /* fill_packet() might have changed the queue */ + queue_map = skb_get_queue_mapping(pkt_dev->skb); + txq = netdev_get_tx_queue(odev, queue_map); + + __netif_tx_lock_bh(txq); + if (!netif_tx_queue_stopped(txq)) { atomic_inc(&(pkt_dev->skb->users)); retry_now: @@ -3377,7 +3396,7 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev) pkt_dev->next_tx_ns = 0; } - netif_tx_unlock_bh(odev); + __netif_tx_unlock_bh(txq); /* If pkt_dev->count is zero, then run forever */ if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) { diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 4e2b865cbba0..2f575b9017d1 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -166,7 +166,7 @@ static inline int qdisc_restart(struct netdev_queue *txq) HARD_TX_LOCK(dev, txq, smp_processor_id()); if (!netif_subqueue_stopped(dev, skb)) - ret = dev_hard_start_xmit(skb, dev); + ret = dev_hard_start_xmit(skb, dev, txq); HARD_TX_UNLOCK(dev, txq); spin_lock(&txq->lock); @@ -198,11 +198,10 @@ static inline int qdisc_restart(struct netdev_queue *txq) void __qdisc_run(struct netdev_queue *txq) { - struct net_device *dev = txq->dev; unsigned long start_time = jiffies; while (qdisc_restart(txq)) { - if (netif_queue_stopped(dev)) + if (netif_tx_queue_stopped(txq)) break; /* diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index 44a2c3451f4d..ade3372221c7 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -295,8 +295,7 @@ restart: slave_txq = netdev_get_tx_queue(slave, 0); if (slave_txq->qdisc_sleeping != q) continue; - if (netif_queue_stopped(slave) || - __netif_subqueue_stopped(slave, subq) || + if (__netif_subqueue_stopped(slave, subq) || !netif_running(slave)) { busy = 1; continue; @@ -305,8 +304,7 @@ restart: switch (teql_resolve(skb, skb_res, slave)) { case 0: if (netif_tx_trylock(slave)) { - if (!netif_queue_stopped(slave) && - !__netif_subqueue_stopped(slave, subq) && + if (!__netif_subqueue_stopped(slave, subq) && slave->hard_start_xmit(skb, slave) == 0) { netif_tx_unlock(slave); master->slaves = NEXT_SLAVE(q); -- cgit v1.2.3 From 6b0fb1261a4655613bed5dac0e935e733969e999 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 15 Jul 2008 02:58:10 -0700 Subject: netdev: Kill struct net_device_subqueue and netdev->egress_subqueue* No longer used. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index b5c1e7df64fc..a1c2c2204498 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -118,14 +118,6 @@ struct wireless_dev; #endif /* __KERNEL__ */ -struct net_device_subqueue -{ - /* Give a control state for each queue. This struct may contain - * per-queue locks in the future. - */ - unsigned long state; -}; - /* * Network device statistics. Akin to the 2.0 ether stats but * with byte counters. @@ -761,10 +753,6 @@ struct net_device /* for setting kernel sock attribute on TCP connection setup */ #define GSO_MAX_SIZE 65536 unsigned int gso_max_size; - - /* The TX queue control structures */ - unsigned int egress_subqueue_count; - struct net_device_subqueue egress_subqueue[1]; }; #define to_net_dev(d) container_of(d, struct net_device, dev) -- cgit v1.2.3 From e3c50d5d25ac09efd9acbe2b2a3e365466de84ed Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 15 Jul 2008 02:58:39 -0700 Subject: netdev: netdev_priv() can now be sane again. The private area of a netdev is now at a fixed offset once more. Unfortunately, some assumptions that netdev_priv() == netdev->priv crept back into the tree. In particular this happened in the loopback driver. Make it use netdev->ml_priv. Signed-off-by: David S. Miller --- drivers/net/loopback.c | 8 ++++---- include/linux/netdevice.h | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index 41b774baac4d..49f6bc036a92 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -153,7 +153,7 @@ static int loopback_xmit(struct sk_buff *skb, struct net_device *dev) dev->last_rx = jiffies; /* it's OK to use per_cpu_ptr() because BHs are off */ - pcpu_lstats = netdev_priv(dev); + pcpu_lstats = dev->ml_priv; lb_stats = per_cpu_ptr(pcpu_lstats, smp_processor_id()); lb_stats->bytes += skb->len; lb_stats->packets++; @@ -171,7 +171,7 @@ static struct net_device_stats *get_stats(struct net_device *dev) unsigned long packets = 0; int i; - pcpu_lstats = netdev_priv(dev); + pcpu_lstats = dev->ml_priv; for_each_possible_cpu(i) { const struct pcpu_lstats *lb_stats; @@ -207,13 +207,13 @@ static int loopback_dev_init(struct net_device *dev) if (!lstats) return -ENOMEM; - dev->priv = lstats; + dev->ml_priv = lstats; return 0; } static void loopback_dev_free(struct net_device *dev) { - struct pcpu_lstats *lstats = netdev_priv(dev); + struct pcpu_lstats *lstats = dev->ml_priv; free_percpu(lstats); free_netdev(dev); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index a1c2c2204498..fdac1159253e 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -808,7 +808,9 @@ void dev_net_set(struct net_device *dev, struct net *net) */ static inline void *netdev_priv(const struct net_device *dev) { - return dev->priv; + return (char *)dev + ((sizeof(struct net_device) + + NETDEV_ALIGN_CONST) + & ~NETDEV_ALIGN_CONST); } /* Set the sysfs physical device reference for the network logical device -- cgit v1.2.3 From eae792b722fef08dcf3aee88266ee7def9710757 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 15 Jul 2008 03:03:33 -0700 Subject: netdev: Add netdev->select_queue() method. Devices or device layers can set this to control the queue selection performed by dev_pick_tx(). This function runs under RCU protection, which allows overriding functions to have some way of synchronizing with things like dynamic ->real_num_tx_queues adjustments. This makes the spinlock prefetch in dev_queue_xmit() a little bit less effective, but that's the price right now for correctness. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 3 +++ net/core/dev.c | 9 ++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index fdac1159253e..9464e6452967 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -724,6 +724,9 @@ struct net_device void (*poll_controller)(struct net_device *dev); #endif + u16 (*select_queue)(struct net_device *dev, + struct sk_buff *skb); + #ifdef CONFIG_NET_NS /* Network namespace this network device is inside */ struct net *nd_net; diff --git a/net/core/dev.c b/net/core/dev.c index f027a1ac4fbb..7ca9564d2f44 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1670,6 +1670,9 @@ static struct netdev_queue *dev_pick_tx(struct net_device *dev, { u16 queue_index = 0; + if (dev->select_queue) + queue_index = dev->select_queue(dev, skb); + skb_set_queue_mapping(skb, queue_index); return netdev_get_tx_queue(dev, queue_index); } @@ -1710,14 +1713,14 @@ int dev_queue_xmit(struct sk_buff *skb) } gso: - txq = dev_pick_tx(dev, skb); - spin_lock_prefetch(&txq->lock); - /* Disable soft irqs for various locks below. Also * stops preemption for RCU. */ rcu_read_lock_bh(); + txq = dev_pick_tx(dev, skb); + spin_lock_prefetch(&txq->lock); + /* Updates of qdisc are serialized by queue->lock. * The struct Qdisc which is pointed to by qdisc is now a * rcu structure - it may be accessed without acquiring -- cgit v1.2.3 From 92831bc395ac8390bf759775c50cb6f90c6eb03d Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 15 Jul 2008 03:48:01 -0700 Subject: netdev: Kill plain netif_schedule() No more users. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 5 ----- 1 file changed, 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 9464e6452967..787fbfc5aebb 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -994,11 +994,6 @@ static inline void netif_schedule_queue(struct netdev_queue *txq) __netif_schedule(txq); } -static inline void netif_schedule(struct net_device *dev) -{ - netif_schedule_queue(netdev_get_tx_queue(dev, 0)); -} - static inline void netif_tx_schedule_all(struct net_device *dev) { unsigned int i; -- cgit v1.2.3 From d3b753db7c4f1f37a98b51974d484fda5d86dab5 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 15 Jul 2008 20:14:35 -0700 Subject: pkt_sched: Move gso_skb into Qdisc. We liberate any dangling gso_skb during qdisc destruction. It really only matters for the root qdisc. But when qdiscs can be shared by multiple netdev_queue objects, we can't have the gso_skb in the netdev_queue any more. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 - include/net/sch_generic.h | 1 + net/sched/sch_generic.c | 19 +++++++++---------- 3 files changed, 10 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 787fbfc5aebb..0883fcf2d16a 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -449,7 +449,6 @@ struct netdev_queue { struct net_device *dev; struct Qdisc *qdisc; unsigned long state; - struct sk_buff *gso_skb; spinlock_t _xmit_lock; int xmit_lock_owner; struct Qdisc *qdisc_sleeping; diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index b47f556c66f8..b96c3d9e10a8 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -36,6 +36,7 @@ struct Qdisc u32 handle; u32 parent; atomic_t refcnt; + struct sk_buff *gso_skb; struct sk_buff_head q; struct netdev_queue *dev_queue; struct list_head list; diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 2f575b9017d1..2bd75befa066 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -77,7 +77,7 @@ static inline int dev_requeue_skb(struct sk_buff *skb, struct Qdisc *q) { if (unlikely(skb->next)) - dev_queue->gso_skb = skb; + q->gso_skb = skb; else q->ops->requeue(skb, q); @@ -85,13 +85,12 @@ static inline int dev_requeue_skb(struct sk_buff *skb, return 0; } -static inline struct sk_buff *dequeue_skb(struct netdev_queue *dev_queue, - struct Qdisc *q) +static inline struct sk_buff *dequeue_skb(struct Qdisc *q) { struct sk_buff *skb; - if ((skb = dev_queue->gso_skb)) - dev_queue->gso_skb = NULL; + if ((skb = q->gso_skb)) + q->gso_skb = NULL; else skb = q->dequeue(q); @@ -155,10 +154,9 @@ static inline int qdisc_restart(struct netdev_queue *txq) struct sk_buff *skb; /* Dequeue packet */ - if (unlikely((skb = dequeue_skb(txq, q)) == NULL)) + if (unlikely((skb = dequeue_skb(q)) == NULL)) return 0; - /* And release queue */ spin_unlock(&txq->lock); @@ -643,8 +641,8 @@ static void dev_deactivate_queue(struct net_device *dev, void *_qdisc_default) { struct Qdisc *qdisc_default = _qdisc_default; + struct sk_buff *skb = NULL; struct Qdisc *qdisc; - struct sk_buff *skb; spin_lock_bh(&dev_queue->lock); @@ -652,9 +650,10 @@ static void dev_deactivate_queue(struct net_device *dev, if (qdisc) { dev_queue->qdisc = qdisc_default; qdisc_reset(qdisc); + + skb = qdisc->gso_skb; + qdisc->gso_skb = NULL; } - skb = dev_queue->gso_skb; - dev_queue->gso_skb = NULL; spin_unlock_bh(&dev_queue->lock); -- cgit v1.2.3 From e2627c8c2241bce45e368e150654d076b58a4595 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 16 Jul 2008 00:56:32 -0700 Subject: pkt_sched: Make QDISC_RUNNING a qdisc state. Currently it is associated with a netdev_queue, but when we have qdisc sharing that no longer makes any sense. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 - include/net/pkt_sched.h | 4 +++- include/net/sch_generic.h | 6 ++++++ net/sched/sch_generic.c | 18 ++++++++++-------- 4 files changed, 19 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 0883fcf2d16a..9240a95793be 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -441,7 +441,6 @@ static inline void napi_synchronize(const struct napi_struct *n) enum netdev_queue_state_t { __QUEUE_STATE_XOFF, - __QUEUE_STATE_QDISC_RUNNING, }; struct netdev_queue { diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h index cb9527815606..06a442d85186 100644 --- a/include/net/pkt_sched.h +++ b/include/net/pkt_sched.h @@ -88,8 +88,10 @@ extern void __qdisc_run(struct netdev_queue *txq); static inline void qdisc_run(struct netdev_queue *txq) { + struct Qdisc *q = txq->qdisc; + if (!netif_tx_queue_stopped(txq) && - !test_and_set_bit(__QUEUE_STATE_QDISC_RUNNING, &txq->state)) + !test_and_set_bit(__QDISC_STATE_RUNNING, &q->state)) __qdisc_run(txq); } diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index b96c3d9e10a8..bc2a09da21b1 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -23,6 +23,11 @@ struct qdisc_rate_table int refcnt; }; +enum qdisc_state_t +{ + __QDISC_STATE_RUNNING, +}; + struct Qdisc { int (*enqueue)(struct sk_buff *skb, struct Qdisc *dev); @@ -36,6 +41,7 @@ struct Qdisc u32 handle; u32 parent; atomic_t refcnt; + unsigned long state; struct sk_buff *gso_skb; struct sk_buff_head q; struct netdev_queue *dev_queue; diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 2bd75befa066..ac208c2b2d10 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -130,8 +130,8 @@ static inline int handle_dev_cpu_collision(struct sk_buff *skb, /* * NOTE: Called under queue->lock with locally disabled BH. * - * __QUEUE_STATE_QDISC_RUNNING guarantees only one CPU can process - * this queue at a time. queue->lock serializes queue accesses for + * __QDISC_STATE_RUNNING guarantees only one CPU can process + * this qdisc at a time. queue->lock serializes queue accesses for * this queue AND txq->qdisc pointer itself. * * netif_tx_lock serializes accesses to device driver. @@ -146,9 +146,9 @@ static inline int handle_dev_cpu_collision(struct sk_buff *skb, * >0 - queue is not empty. * */ -static inline int qdisc_restart(struct netdev_queue *txq) +static inline int qdisc_restart(struct netdev_queue *txq, + struct Qdisc *q) { - struct Qdisc *q = txq->qdisc; int ret = NETDEV_TX_BUSY; struct net_device *dev; struct sk_buff *skb; @@ -168,7 +168,6 @@ static inline int qdisc_restart(struct netdev_queue *txq) HARD_TX_UNLOCK(dev, txq); spin_lock(&txq->lock); - q = txq->qdisc; switch (ret) { case NETDEV_TX_OK: @@ -197,8 +196,9 @@ static inline int qdisc_restart(struct netdev_queue *txq) void __qdisc_run(struct netdev_queue *txq) { unsigned long start_time = jiffies; + struct Qdisc *q = txq->qdisc; - while (qdisc_restart(txq)) { + while (qdisc_restart(txq, q)) { if (netif_tx_queue_stopped(txq)) break; @@ -213,7 +213,7 @@ void __qdisc_run(struct netdev_queue *txq) } } - clear_bit(__QUEUE_STATE_QDISC_RUNNING, &txq->state); + clear_bit(__QDISC_STATE_RUNNING, &q->state); } static void dev_watchdog(unsigned long arg) @@ -666,14 +666,16 @@ static bool some_qdisc_is_running(struct net_device *dev, int lock) for (i = 0; i < dev->num_tx_queues; i++) { struct netdev_queue *dev_queue; + struct Qdisc *q; int val; dev_queue = netdev_get_tx_queue(dev, i); + q = dev_queue->qdisc; if (lock) spin_lock_bh(&dev_queue->lock); - val = test_bit(__QUEUE_STATE_QDISC_RUNNING, &dev_queue->state); + val = test_bit(__QDISC_STATE_RUNNING, &q->state); if (lock) spin_unlock_bh(&dev_queue->lock); -- cgit v1.2.3 From 37437bb2e1ae8af470dfcd5b4ff454110894ccaf Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 16 Jul 2008 02:15:04 -0700 Subject: pkt_sched: Schedule qdiscs instead of netdev_queue. When we have shared qdiscs, packets come out of the qdiscs for multiple transmit queues. Therefore it doesn't make any sense to schedule the transmit queue when logically we cannot know ahead of time the TX queue of the SKB that the qdisc->dequeue() will give us. Just for sanity I added a BUG check to make sure we never get into a state where the noop_qdisc is scheduled. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 12 ++++----- include/net/pkt_sched.h | 11 +++----- include/net/sch_generic.h | 2 ++ net/core/dev.c | 68 +++++++++++++++++++---------------------------- net/sched/sch_api.c | 3 +-- net/sched/sch_cbq.c | 2 +- net/sched/sch_generic.c | 30 ++++++++++----------- 7 files changed, 55 insertions(+), 73 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 9240a95793be..1e839fa01434 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -275,7 +275,6 @@ enum netdev_state_t { __LINK_STATE_START, __LINK_STATE_PRESENT, - __LINK_STATE_SCHED, __LINK_STATE_NOCARRIER, __LINK_STATE_LINKWATCH_PENDING, __LINK_STATE_DORMANT, @@ -452,7 +451,6 @@ struct netdev_queue { int xmit_lock_owner; struct Qdisc *qdisc_sleeping; struct list_head qdisc_list; - struct netdev_queue *next_sched; } ____cacheline_aligned_in_smp; /* @@ -969,7 +967,7 @@ static inline int unregister_gifconf(unsigned int family) */ struct softnet_data { - struct netdev_queue *output_queue; + struct Qdisc *output_queue; struct sk_buff_head input_pkt_queue; struct list_head poll_list; struct sk_buff *completion_queue; @@ -984,12 +982,12 @@ DECLARE_PER_CPU(struct softnet_data,softnet_data); #define HAVE_NETIF_QUEUE -extern void __netif_schedule(struct netdev_queue *txq); +extern void __netif_schedule(struct Qdisc *q); static inline void netif_schedule_queue(struct netdev_queue *txq) { if (!test_bit(__QUEUE_STATE_XOFF, &txq->state)) - __netif_schedule(txq); + __netif_schedule(txq->qdisc); } static inline void netif_tx_schedule_all(struct net_device *dev) @@ -1042,7 +1040,7 @@ static inline void netif_tx_wake_queue(struct netdev_queue *dev_queue) } #endif if (test_and_clear_bit(__QUEUE_STATE_XOFF, &dev_queue->state)) - __netif_schedule(dev_queue); + __netif_schedule(dev_queue->qdisc); } static inline void netif_wake_queue(struct net_device *dev) @@ -1186,7 +1184,7 @@ static inline void netif_wake_subqueue(struct net_device *dev, u16 queue_index) return; #endif if (test_and_clear_bit(__QUEUE_STATE_XOFF, &txq->state)) - __netif_schedule(txq); + __netif_schedule(txq->qdisc); } /** diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h index 06a442d85186..e4e30052e4e2 100644 --- a/include/net/pkt_sched.h +++ b/include/net/pkt_sched.h @@ -84,15 +84,12 @@ extern struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r, struct nlattr *tab); extern void qdisc_put_rtab(struct qdisc_rate_table *tab); -extern void __qdisc_run(struct netdev_queue *txq); +extern void __qdisc_run(struct Qdisc *q); -static inline void qdisc_run(struct netdev_queue *txq) +static inline void qdisc_run(struct Qdisc *q) { - struct Qdisc *q = txq->qdisc; - - if (!netif_tx_queue_stopped(txq) && - !test_and_set_bit(__QDISC_STATE_RUNNING, &q->state)) - __qdisc_run(txq); + if (!test_and_set_bit(__QDISC_STATE_RUNNING, &q->state)) + __qdisc_run(q); } extern int tc_classify_compat(struct sk_buff *skb, struct tcf_proto *tp, diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 92417825d387..3cc4b5cd8c6a 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -26,6 +26,7 @@ struct qdisc_rate_table enum qdisc_state_t { __QDISC_STATE_RUNNING, + __QDISC_STATE_SCHED, }; struct Qdisc @@ -45,6 +46,7 @@ struct Qdisc struct sk_buff *gso_skb; struct sk_buff_head q; struct netdev_queue *dev_queue; + struct Qdisc *next_sched; struct list_head list; struct gnet_stats_basic bstats; diff --git a/net/core/dev.c b/net/core/dev.c index 467bfb325123..0b909b74f698 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1323,18 +1323,18 @@ static void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev) } -void __netif_schedule(struct netdev_queue *txq) +void __netif_schedule(struct Qdisc *q) { - struct net_device *dev = txq->dev; + BUG_ON(q == &noop_qdisc); - if (!test_and_set_bit(__LINK_STATE_SCHED, &dev->state)) { + if (!test_and_set_bit(__QDISC_STATE_SCHED, &q->state)) { struct softnet_data *sd; unsigned long flags; local_irq_save(flags); sd = &__get_cpu_var(softnet_data); - txq->next_sched = sd->output_queue; - sd->output_queue = txq; + q->next_sched = sd->output_queue; + sd->output_queue = q; raise_softirq_irqoff(NET_TX_SOFTIRQ); local_irq_restore(flags); } @@ -1771,37 +1771,23 @@ gso: rcu_read_lock_bh(); txq = dev_pick_tx(dev, skb); - spin_lock_prefetch(&txq->lock); - - /* Updates of qdisc are serialized by queue->lock. - * The struct Qdisc which is pointed to by qdisc is now a - * rcu structure - it may be accessed without acquiring - * a lock (but the structure may be stale.) The freeing of the - * qdisc will be deferred until it's known that there are no - * more references to it. - * - * If the qdisc has an enqueue function, we still need to - * hold the queue->lock before calling it, since queue->lock - * also serializes access to the device queue. - */ - q = rcu_dereference(txq->qdisc); + #ifdef CONFIG_NET_CLS_ACT skb->tc_verd = SET_TC_AT(skb->tc_verd,AT_EGRESS); #endif if (q->enqueue) { - /* Grab device queue */ - spin_lock(&txq->lock); - q = txq->qdisc; - if (q->enqueue) { - rc = q->enqueue(skb, q); - qdisc_run(txq); - spin_unlock(&txq->lock); - - rc = rc == NET_XMIT_BYPASS ? NET_XMIT_SUCCESS : rc; - goto out; - } - spin_unlock(&txq->lock); + spinlock_t *root_lock = qdisc_root_lock(q); + + spin_lock(root_lock); + + rc = q->enqueue(skb, q); + qdisc_run(q); + + spin_unlock(root_lock); + + rc = rc == NET_XMIT_BYPASS ? NET_XMIT_SUCCESS : rc; + goto out; } /* The device has no queue. Common case for software devices: @@ -1974,7 +1960,7 @@ static void net_tx_action(struct softirq_action *h) } if (sd->output_queue) { - struct netdev_queue *head; + struct Qdisc *head; local_irq_disable(); head = sd->output_queue; @@ -1982,18 +1968,20 @@ static void net_tx_action(struct softirq_action *h) local_irq_enable(); while (head) { - struct netdev_queue *txq = head; - struct net_device *dev = txq->dev; + struct Qdisc *q = head; + spinlock_t *root_lock; + head = head->next_sched; smp_mb__before_clear_bit(); - clear_bit(__LINK_STATE_SCHED, &dev->state); + clear_bit(__QDISC_STATE_SCHED, &q->state); - if (spin_trylock(&txq->lock)) { - qdisc_run(txq); - spin_unlock(&txq->lock); + root_lock = qdisc_root_lock(q); + if (spin_trylock(root_lock)) { + qdisc_run(q); + spin_unlock(root_lock); } else { - netif_schedule_queue(txq); + __netif_schedule(q); } } } @@ -4459,7 +4447,7 @@ static int dev_cpu_callback(struct notifier_block *nfb, void *ocpu) { struct sk_buff **list_skb; - struct netdev_queue **list_net; + struct Qdisc **list_net; struct sk_buff *skb; unsigned int cpu, oldcpu = (unsigned long)ocpu; struct softnet_data *sd, *oldsd; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 19c244a00839..8e8c5becc348 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -294,11 +294,10 @@ static enum hrtimer_restart qdisc_watchdog(struct hrtimer *timer) { struct qdisc_watchdog *wd = container_of(timer, struct qdisc_watchdog, timer); - struct netdev_queue *txq = wd->qdisc->dev_queue; wd->qdisc->flags &= ~TCQ_F_THROTTLED; smp_wmb(); - netif_schedule_queue(txq); + __netif_schedule(wd->qdisc); return HRTIMER_NORESTART; } diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index 37ae653db683..a3953bbe2d79 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -650,7 +650,7 @@ static enum hrtimer_restart cbq_undelay(struct hrtimer *timer) } sch->flags &= ~TCQ_F_THROTTLED; - netif_schedule_queue(sch->dev_queue); + __netif_schedule(sch); return HRTIMER_NORESTART; } diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 739a8711ab30..dd5c4e70abe4 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -72,16 +72,14 @@ static inline int qdisc_qlen(struct Qdisc *q) return q->q.qlen; } -static inline int dev_requeue_skb(struct sk_buff *skb, - struct netdev_queue *dev_queue, - struct Qdisc *q) +static inline int dev_requeue_skb(struct sk_buff *skb, struct Qdisc *q) { if (unlikely(skb->next)) q->gso_skb = skb; else q->ops->requeue(skb, q); - netif_schedule_queue(dev_queue); + __netif_schedule(q); return 0; } @@ -121,7 +119,7 @@ static inline int handle_dev_cpu_collision(struct sk_buff *skb, * some time. */ __get_cpu_var(netdev_rx_stat).cpu_collision++; - ret = dev_requeue_skb(skb, dev_queue, q); + ret = dev_requeue_skb(skb, q); } return ret; @@ -146,9 +144,9 @@ static inline int handle_dev_cpu_collision(struct sk_buff *skb, * >0 - queue is not empty. * */ -static inline int qdisc_restart(struct netdev_queue *txq, - struct Qdisc *q) +static inline int qdisc_restart(struct Qdisc *q) { + struct netdev_queue *txq; int ret = NETDEV_TX_BUSY; struct net_device *dev; spinlock_t *root_lock; @@ -163,7 +161,8 @@ static inline int qdisc_restart(struct netdev_queue *txq, /* And release qdisc */ spin_unlock(root_lock); - dev = txq->dev; + dev = qdisc_dev(q); + txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb)); HARD_TX_LOCK(dev, txq, smp_processor_id()); if (!netif_subqueue_stopped(dev, skb)) @@ -189,29 +188,28 @@ static inline int qdisc_restart(struct netdev_queue *txq, printk(KERN_WARNING "BUG %s code %d qlen %d\n", dev->name, ret, q->q.qlen); - ret = dev_requeue_skb(skb, txq, q); + ret = dev_requeue_skb(skb, q); break; } + if (ret && netif_tx_queue_stopped(txq)) + ret = 0; + return ret; } -void __qdisc_run(struct netdev_queue *txq) +void __qdisc_run(struct Qdisc *q) { unsigned long start_time = jiffies; - struct Qdisc *q = txq->qdisc; - - while (qdisc_restart(txq, q)) { - if (netif_tx_queue_stopped(txq)) - break; + while (qdisc_restart(q)) { /* * Postpone processing if * 1. another process needs the CPU; * 2. we've been doing it for too long. */ if (need_resched() || jiffies != start_time) { - netif_schedule_queue(txq); + __netif_schedule(q); break; } } -- cgit v1.2.3 From ead81cc5fc6d996db6afb20f211241612610a07a Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 17 Jul 2008 00:50:32 -0700 Subject: netdevice: Move qdisc_list back into net_device proper. And give it it's own lock. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 3 ++- net/core/dev.c | 2 ++ net/sched/sch_api.c | 31 +++++++------------------------ net/sched/sch_generic.c | 9 +++++++-- 4 files changed, 18 insertions(+), 27 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 1e839fa01434..3170bcef734b 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -450,7 +450,6 @@ struct netdev_queue { spinlock_t _xmit_lock; int xmit_lock_owner; struct Qdisc *qdisc_sleeping; - struct list_head qdisc_list; } ____cacheline_aligned_in_smp; /* @@ -638,6 +637,8 @@ struct net_device unsigned int real_num_tx_queues; unsigned long tx_queue_len; /* Max frames per queue allowed */ + spinlock_t qdisc_list_lock; + struct list_head qdisc_list; /* * One part is mostly used on xmit path (device) diff --git a/net/core/dev.c b/net/core/dev.c index 0b909b74f698..6741e344ac59 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3886,6 +3886,8 @@ int register_netdevice(struct net_device *dev) net = dev_net(dev); spin_lock_init(&dev->addr_list_lock); + spin_lock_init(&dev->qdisc_list_lock); + INIT_LIST_HEAD(&dev->qdisc_list); netdev_init_queue_locks(dev); dev->iflink = -1; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 8e8c5becc348..6958fe7c9a77 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -183,30 +183,17 @@ EXPORT_SYMBOL(unregister_qdisc); (root qdisc, all its children, children of children etc.) */ -static struct Qdisc *__qdisc_lookup(struct netdev_queue *dev_queue, u32 handle) +struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle) { struct Qdisc *q; - list_for_each_entry(q, &dev_queue->qdisc_list, list) { + list_for_each_entry(q, &dev->qdisc_list, list) { if (q->handle == handle) return q; } return NULL; } -struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle) -{ - unsigned int i; - - for (i = 0; i < dev->num_tx_queues; i++) { - struct netdev_queue *txq = netdev_get_tx_queue(dev, i); - struct Qdisc *q = __qdisc_lookup(txq, handle); - if (q) - return q; - } - return NULL; -} - static struct Qdisc *qdisc_leaf(struct Qdisc *p, u32 classid) { unsigned long cl; @@ -645,9 +632,9 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue, goto err_out3; } } - qdisc_lock_tree(dev); - list_add_tail(&sch->list, &dev_queue->qdisc_list); - qdisc_unlock_tree(dev); + spin_lock_bh(&dev->qdisc_list_lock); + list_add_tail(&sch->list, &dev->qdisc_list); + spin_unlock_bh(&dev->qdisc_list_lock); return sch; } @@ -1032,14 +1019,12 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb) read_lock(&dev_base_lock); idx = 0; for_each_netdev(&init_net, dev) { - struct netdev_queue *dev_queue; if (idx < s_idx) goto cont; if (idx > s_idx) s_q_idx = 0; q_idx = 0; - dev_queue = netdev_get_tx_queue(dev, 0); - list_for_each_entry(q, &dev_queue->qdisc_list, list) { + list_for_each_entry(q, &dev->qdisc_list, list) { if (q_idx < s_q_idx) { q_idx++; continue; @@ -1269,7 +1254,6 @@ static int qdisc_class_dump(struct Qdisc *q, unsigned long cl, struct qdisc_walk static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb) { struct net *net = sock_net(skb->sk); - struct netdev_queue *dev_queue; int t; int s_t; struct net_device *dev; @@ -1288,8 +1272,7 @@ static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb) s_t = cb->args[0]; t = 0; - dev_queue = netdev_get_tx_queue(dev, 0); - list_for_each_entry(q, &dev_queue->qdisc_list, list) { + list_for_each_entry(q, &dev->qdisc_list, list) { if (t < s_t || !q->ops->cl_ops || (tcm->tcm_parent && TC_H_MAJ(tcm->tcm_parent) != q->handle)) { diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index efa418a1b34e..8cdf0b4a6a5a 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -563,11 +563,15 @@ static void __qdisc_destroy(struct rcu_head *head) void qdisc_destroy(struct Qdisc *qdisc) { + struct net_device *dev = qdisc_dev(qdisc); + if (qdisc->flags & TCQ_F_BUILTIN || !atomic_dec_and_test(&qdisc->refcnt)) return; + spin_lock_bh(&dev->qdisc_list_lock); list_del(&qdisc->list); + spin_unlock_bh(&dev->qdisc_list_lock); call_rcu(&qdisc->q_rcu, __qdisc_destroy); } @@ -599,7 +603,9 @@ static void attach_one_default_qdisc(struct net_device *dev, printk(KERN_INFO "%s: activation failed\n", dev->name); return; } - list_add_tail(&qdisc->list, &dev_queue->qdisc_list); + spin_lock_bh(&dev->qdisc_list_lock); + list_add_tail(&qdisc->list, &dev->qdisc_list); + spin_unlock_bh(&dev->qdisc_list_lock); } else { qdisc = &noqueue_qdisc; } @@ -738,7 +744,6 @@ static void dev_init_scheduler_queue(struct net_device *dev, dev_queue->qdisc = qdisc; dev_queue->qdisc_sleeping = qdisc; - INIT_LIST_HEAD(&dev_queue->qdisc_list); } void dev_init_scheduler(struct net_device *dev) -- cgit v1.2.3 From 83874000929ed63aef30b44083a9f713135ff040 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 17 Jul 2008 00:53:03 -0700 Subject: pkt_sched: Kill netdev_queue lock. We can simply use the qdisc->q.lock for all of the qdisc tree synchronization. Signed-off-by: David S. Miller --- drivers/net/ifb.c | 20 -------------------- include/linux/netdevice.h | 1 - include/net/sch_generic.h | 7 ++++++- net/core/dev.c | 9 +++++---- net/mac80211/wme.c | 19 ++++++++++++------- net/sched/sch_generic.c | 32 +++++++++++++++----------------- net/sched/sch_teql.c | 7 +++++-- 7 files changed, 43 insertions(+), 52 deletions(-) (limited to 'include/linux') diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c index 897b05e79ed0..0960e69b2da4 100644 --- a/drivers/net/ifb.c +++ b/drivers/net/ifb.c @@ -35,7 +35,6 @@ #include #include #include -#include #define TX_TIMEOUT (2*HZ) @@ -228,22 +227,6 @@ static struct rtnl_link_ops ifb_link_ops __read_mostly = { module_param(numifbs, int, 0); MODULE_PARM_DESC(numifbs, "Number of ifb devices"); -/* - * dev_ifb's TX queue lock is usually taken after dev->rx_queue.lock, - * reversely to e.g. qdisc_lock_tree(). It should be safe until - * ifb doesn't take dev's TX queue lock with dev_ifb->rx_queue.lock. - * But lockdep should know that ifb has different locks from dev. - */ -static struct lock_class_key ifb_tx_queue_lock_key; -static struct lock_class_key ifb_rx_queue_lock_key; - -static void set_tx_lockdep_key(struct net_device *dev, - struct netdev_queue *txq, - void *_unused) -{ - lockdep_set_class(&txq->lock, &ifb_tx_queue_lock_key); -} - static int __init ifb_init_one(int index) { struct net_device *dev_ifb; @@ -264,9 +247,6 @@ static int __init ifb_init_one(int index) if (err < 0) goto err; - netdev_for_each_tx_queue(dev_ifb, set_tx_lockdep_key, NULL); - lockdep_set_class(&dev_ifb->rx_queue.lock, &ifb_rx_queue_lock_key); - return 0; err: diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 3170bcef734b..9c5a68850114 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -443,7 +443,6 @@ enum netdev_queue_state_t }; struct netdev_queue { - spinlock_t lock; struct net_device *dev; struct Qdisc *qdisc; unsigned long state; diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 1eef8d0c9990..2902a42564f0 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -163,6 +163,11 @@ struct tcf_proto struct tcf_proto_ops *ops; }; +static inline spinlock_t *qdisc_lock(struct Qdisc *qdisc) +{ + return &qdisc->q.lock; +} + static inline struct Qdisc *qdisc_root(struct Qdisc *qdisc) { return qdisc->dev_queue->qdisc; @@ -172,7 +177,7 @@ static inline spinlock_t *qdisc_root_lock(struct Qdisc *qdisc) { struct Qdisc *root = qdisc_root(qdisc); - return &root->dev_queue->lock; + return qdisc_lock(root); } static inline struct net_device *qdisc_dev(struct Qdisc *qdisc) diff --git a/net/core/dev.c b/net/core/dev.c index 6741e344ac59..32a13772c1cb 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2080,10 +2080,12 @@ static int ing_filter(struct sk_buff *skb) rxq = &dev->rx_queue; - spin_lock(&rxq->lock); - if ((q = rxq->qdisc) != NULL) + q = rxq->qdisc; + if (q) { + spin_lock(qdisc_lock(q)); result = q->enqueue(skb, q); - spin_unlock(&rxq->lock); + spin_unlock(qdisc_lock(q)); + } return result; } @@ -4173,7 +4175,6 @@ static void netdev_init_one_queue(struct net_device *dev, struct netdev_queue *queue, void *_unused) { - spin_lock_init(&queue->lock); queue->dev = dev; } diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index b21cfec4b6ce..6e8099e77043 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -237,12 +237,14 @@ void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local, ieee80211_requeue(local, agg_queue); } else { struct netdev_queue *txq; + spinlock_t *root_lock; txq = netdev_get_tx_queue(local->mdev, agg_queue); + root_lock = qdisc_root_lock(txq->qdisc); - spin_lock_bh(&txq->lock); + spin_lock_bh(root_lock); qdisc_reset(txq->qdisc); - spin_unlock_bh(&txq->lock); + spin_unlock_bh(root_lock); } } @@ -250,6 +252,7 @@ void ieee80211_requeue(struct ieee80211_local *local, int queue) { struct netdev_queue *txq = netdev_get_tx_queue(local->mdev, queue); struct sk_buff_head list; + spinlock_t *root_lock; struct Qdisc *qdisc; u32 len; @@ -261,14 +264,15 @@ void ieee80211_requeue(struct ieee80211_local *local, int queue) skb_queue_head_init(&list); - spin_lock(&txq->lock); + root_lock = qdisc_root_lock(qdisc); + spin_lock(root_lock); for (len = qdisc->q.qlen; len > 0; len--) { struct sk_buff *skb = qdisc->dequeue(qdisc); if (skb) __skb_queue_tail(&list, skb); } - spin_unlock(&txq->lock); + spin_unlock(root_lock); for (len = list.qlen; len > 0; len--) { struct sk_buff *skb = __skb_dequeue(&list); @@ -280,12 +284,13 @@ void ieee80211_requeue(struct ieee80211_local *local, int queue) txq = netdev_get_tx_queue(local->mdev, new_queue); - spin_lock(&txq->lock); qdisc = rcu_dereference(txq->qdisc); - qdisc->enqueue(skb, qdisc); + root_lock = qdisc_root_lock(qdisc); - spin_unlock(&txq->lock); + spin_lock(root_lock); + qdisc->enqueue(skb, qdisc); + spin_unlock(root_lock); } out_unlock: diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 3d53e92ad9c8..8fc580b3e173 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -96,15 +96,15 @@ static inline int handle_dev_cpu_collision(struct sk_buff *skb, } /* - * NOTE: Called under queue->lock with locally disabled BH. + * NOTE: Called under qdisc_lock(q) with locally disabled BH. * * __QDISC_STATE_RUNNING guarantees only one CPU can process - * this qdisc at a time. queue->lock serializes queue accesses for - * this queue AND txq->qdisc pointer itself. + * this qdisc at a time. qdisc_lock(q) serializes queue accesses for + * this queue. * * netif_tx_lock serializes accesses to device driver. * - * queue->lock and netif_tx_lock are mutually exclusive, + * qdisc_lock(q) and netif_tx_lock are mutually exclusive, * if one is grabbed, another must be free. * * Note, that this procedure can be called by a watchdog timer @@ -317,7 +317,6 @@ struct Qdisc_ops noop_qdisc_ops __read_mostly = { }; static struct netdev_queue noop_netdev_queue = { - .lock = __SPIN_LOCK_UNLOCKED(noop_netdev_queue.lock), .qdisc = &noop_qdisc, }; @@ -327,6 +326,7 @@ struct Qdisc noop_qdisc = { .flags = TCQ_F_BUILTIN, .ops = &noop_qdisc_ops, .list = LIST_HEAD_INIT(noop_qdisc.list), + .q.lock = __SPIN_LOCK_UNLOCKED(noop_qdisc.q.lock), .dev_queue = &noop_netdev_queue, }; EXPORT_SYMBOL(noop_qdisc); @@ -498,7 +498,7 @@ errout: } EXPORT_SYMBOL(qdisc_create_dflt); -/* Under queue->lock and BH! */ +/* Under qdisc_root_lock(qdisc) and BH! */ void qdisc_reset(struct Qdisc *qdisc) { @@ -526,10 +526,12 @@ static void __qdisc_destroy(struct rcu_head *head) module_put(ops->owner); dev_put(qdisc_dev(qdisc)); + kfree_skb(qdisc->gso_skb); + kfree((char *) qdisc - qdisc->padded); } -/* Under queue->lock and BH! */ +/* Under qdisc_root_lock(qdisc) and BH! */ void qdisc_destroy(struct Qdisc *qdisc) { @@ -586,13 +588,12 @@ static void transition_one_qdisc(struct net_device *dev, struct netdev_queue *dev_queue, void *_need_watchdog) { + struct Qdisc *new_qdisc = dev_queue->qdisc_sleeping; int *need_watchdog_p = _need_watchdog; - spin_lock_bh(&dev_queue->lock); - rcu_assign_pointer(dev_queue->qdisc, dev_queue->qdisc_sleeping); - if (dev_queue->qdisc != &noqueue_qdisc) + rcu_assign_pointer(dev_queue->qdisc, new_qdisc); + if (new_qdisc != &noqueue_qdisc) *need_watchdog_p = 1; - spin_unlock_bh(&dev_queue->lock); } void dev_activate(struct net_device *dev) @@ -629,19 +630,16 @@ static void dev_deactivate_queue(struct net_device *dev, struct sk_buff *skb = NULL; struct Qdisc *qdisc; - spin_lock_bh(&dev_queue->lock); - qdisc = dev_queue->qdisc; if (qdisc) { + spin_lock_bh(qdisc_lock(qdisc)); + dev_queue->qdisc = qdisc_default; qdisc_reset(qdisc); - skb = qdisc->gso_skb; - qdisc->gso_skb = NULL; + spin_unlock_bh(qdisc_lock(qdisc)); } - spin_unlock_bh(&dev_queue->lock); - kfree_skb(skb); } diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index ade3372221c7..8b0ff345f9da 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -156,12 +156,15 @@ teql_destroy(struct Qdisc* sch) master->slaves = NEXT_SLAVE(q); if (q == master->slaves) { struct netdev_queue *txq; + spinlock_t *root_lock; txq = netdev_get_tx_queue(master->dev, 0); master->slaves = NULL; - spin_lock_bh(&txq->lock); + + root_lock = qdisc_root_lock(txq->qdisc); + spin_lock_bh(root_lock); qdisc_reset(txq->qdisc); - spin_unlock_bh(&txq->lock); + spin_unlock_bh(root_lock); } } skb_queue_purge(&dat->q); -- cgit v1.2.3 From de05c557b24c7dffc6d392e3db120cf11c9f6ae7 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Fri, 18 Jul 2008 04:07:21 -0700 Subject: proc: consolidate per-net single_open callers There are already 7 of them - time to kill some duplicate code. Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- fs/proc/proc_net.c | 24 +++++++++++++++++++ include/linux/seq_file_net.h | 2 ++ net/ipv4/fib_trie.c | 13 +--------- net/ipv4/proc.c | 57 +++----------------------------------------- net/ipv6/proc.c | 19 +-------------- net/ipv6/route.c | 26 ++------------------ 6 files changed, 33 insertions(+), 108 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c index 83f357b30d71..ab232ad2e6f9 100644 --- a/fs/proc/proc_net.c +++ b/fs/proc/proc_net.c @@ -51,6 +51,30 @@ int seq_open_net(struct inode *ino, struct file *f, } EXPORT_SYMBOL_GPL(seq_open_net); +int single_open_net(struct inode *inode, struct file *file, + int (*show)(struct seq_file *, void *)) +{ + int err; + struct net *net; + + err = -ENXIO; + net = get_proc_net(inode); + if (net == NULL) + goto err_net; + + err = single_open(file, show, net); + if (err < 0) + goto err_open; + + return 0; + +err_open: + put_net(net); +err_net: + return err; +} +EXPORT_SYMBOL_GPL(single_open_net); + int seq_release_net(struct inode *ino, struct file *f) { struct seq_file *seq; diff --git a/include/linux/seq_file_net.h b/include/linux/seq_file_net.h index 4ac52542a563..87dcc0ecf6eb 100644 --- a/include/linux/seq_file_net.h +++ b/include/linux/seq_file_net.h @@ -14,6 +14,8 @@ struct seq_net_private { int seq_open_net(struct inode *, struct file *, const struct seq_operations *, int); +int single_open_net(struct inode *, struct file *file, + int (*show)(struct seq_file *, void *)); int seq_release_net(struct inode *, struct file *); static inline struct net *seq_file_net(struct seq_file *seq) { diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index f155a66d6ebf..6009df238ed9 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -2251,18 +2251,7 @@ static int fib_triestat_seq_show(struct seq_file *seq, void *v) static int fib_triestat_seq_open(struct inode *inode, struct file *file) { - int err; - struct net *net; - - net = get_proc_net(inode); - if (net == NULL) - return -ENXIO; - err = single_open(file, fib_triestat_seq_show, net); - if (err < 0) { - put_net(net); - return err; - } - return 0; + return single_open_net(inode, file, fib_triestat_seq_show); } static int fib_triestat_seq_release(struct inode *ino, struct file *f) diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 554390431a4e..daf5d3c80cef 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -71,24 +71,7 @@ static int sockstat_seq_show(struct seq_file *seq, void *v) static int sockstat_seq_open(struct inode *inode, struct file *file) { - int err; - struct net *net; - - err = -ENXIO; - net = get_proc_net(inode); - if (net == NULL) - goto err_net; - - err = single_open(file, sockstat_seq_show, net); - if (err < 0) - goto err_open; - - return 0; - -err_open: - put_net(net); -err_net: - return err; + return single_open_net(inode, file, sockstat_seq_show); } static int sockstat_seq_release(struct inode *inode, struct file *file) @@ -397,24 +380,7 @@ static int snmp_seq_show(struct seq_file *seq, void *v) static int snmp_seq_open(struct inode *inode, struct file *file) { - int err; - struct net *net; - - err = -ENXIO; - net = get_proc_net(inode); - if (net == NULL) - goto err_net; - - err = single_open(file, snmp_seq_show, net); - if (err < 0) - goto err_open; - - return 0; - -err_open: - put_net(net); -err_net: - return err; + return single_open_net(inode, file, snmp_seq_show); } static int snmp_seq_release(struct inode *inode, struct file *file) @@ -469,24 +435,7 @@ static int netstat_seq_show(struct seq_file *seq, void *v) static int netstat_seq_open(struct inode *inode, struct file *file) { - int err; - struct net *net; - - err = -ENXIO; - net = get_proc_net(inode); - if (net == NULL) - goto err_net; - - err = single_open(file, netstat_seq_show, net); - if (err < 0) - goto err_open; - - return 0; - -err_open: - put_net(net); -err_net: - return err; + return single_open_net(inode, file, netstat_seq_show); } static int netstat_seq_release(struct inode *inode, struct file *file) diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c index cbc7e514d3ec..29c5a79444c2 100644 --- a/net/ipv6/proc.c +++ b/net/ipv6/proc.c @@ -183,24 +183,7 @@ static int snmp6_seq_show(struct seq_file *seq, void *v) static int sockstat6_seq_open(struct inode *inode, struct file *file) { - int err; - struct net *net; - - err = -ENXIO; - net = get_proc_net(inode); - if (net == NULL) - goto err_net; - - err = single_open(file, sockstat6_seq_show, net); - if (err < 0) - goto err_open; - - return 0; - -err_open: - put_net(net); -err_net: - return err; + return single_open_net(inode, file, sockstat6_seq_show); } static int sockstat6_seq_release(struct inode *inode, struct file *file) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 5d6c166dfbb6..fb7ff8f0c6db 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2416,18 +2416,7 @@ static int ipv6_route_show(struct seq_file *m, void *v) static int ipv6_route_open(struct inode *inode, struct file *file) { - int err; - struct net *net = get_proc_net(inode); - if (!net) - return -ENXIO; - - err = single_open(file, ipv6_route_show, net); - if (err < 0) { - put_net(net); - return err; - } - - return 0; + return single_open_net(inode, file, ipv6_route_show); } static int ipv6_route_release(struct inode *inode, struct file *file) @@ -2463,18 +2452,7 @@ static int rt6_stats_seq_show(struct seq_file *seq, void *v) static int rt6_stats_seq_open(struct inode *inode, struct file *file) { - int err; - struct net *net = get_proc_net(inode); - if (!net) - return -ENXIO; - - err = single_open(file, rt6_stats_seq_show, net); - if (err < 0) { - put_net(net); - return err; - } - - return 0; + return single_open_net(inode, file, rt6_stats_seq_show); } static int rt6_stats_seq_release(struct inode *inode, struct file *file) -- cgit v1.2.3 From b6fcbdb4f283f7ba67cec3cda6be23da8e959031 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov Date: Fri, 18 Jul 2008 04:07:44 -0700 Subject: proc: consolidate per-net single-release callers They are symmetrical to single_open ones :) Signed-off-by: Pavel Emelyanov Signed-off-by: David S. Miller --- fs/proc/proc_net.c | 8 ++++++++ include/linux/seq_file_net.h | 1 + net/ipv4/fib_trie.c | 9 +-------- net/ipv4/proc.c | 30 +++--------------------------- net/ipv6/proc.c | 10 +--------- net/ipv6/route.c | 20 ++------------------ 6 files changed, 16 insertions(+), 62 deletions(-) (limited to 'include/linux') diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c index ab232ad2e6f9..b224a28e0c15 100644 --- a/fs/proc/proc_net.c +++ b/fs/proc/proc_net.c @@ -87,6 +87,14 @@ int seq_release_net(struct inode *ino, struct file *f) } EXPORT_SYMBOL_GPL(seq_release_net); +int single_release_net(struct inode *ino, struct file *f) +{ + struct seq_file *seq = f->private_data; + put_net(seq->private); + return single_release(ino, f); +} +EXPORT_SYMBOL_GPL(single_release_net); + static struct net *get_proc_task_net(struct inode *dir) { struct task_struct *task; diff --git a/include/linux/seq_file_net.h b/include/linux/seq_file_net.h index 87dcc0ecf6eb..32c89bbe24a2 100644 --- a/include/linux/seq_file_net.h +++ b/include/linux/seq_file_net.h @@ -17,6 +17,7 @@ int seq_open_net(struct inode *, struct file *, int single_open_net(struct inode *, struct file *file, int (*show)(struct seq_file *, void *)); int seq_release_net(struct inode *, struct file *); +int single_release_net(struct inode *, struct file *); static inline struct net *seq_file_net(struct seq_file *seq) { #ifdef CONFIG_NET_NS diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 6009df238ed9..5cb72786a8af 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -2254,19 +2254,12 @@ static int fib_triestat_seq_open(struct inode *inode, struct file *file) return single_open_net(inode, file, fib_triestat_seq_show); } -static int fib_triestat_seq_release(struct inode *ino, struct file *f) -{ - struct seq_file *seq = f->private_data; - put_net(seq->private); - return single_release(ino, f); -} - static const struct file_operations fib_triestat_fops = { .owner = THIS_MODULE, .open = fib_triestat_seq_open, .read = seq_read, .llseek = seq_lseek, - .release = fib_triestat_seq_release, + .release = single_release_net, }; static struct node *fib_trie_get_idx(struct seq_file *seq, loff_t pos) diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index daf5d3c80cef..834356ea99df 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -74,20 +74,12 @@ static int sockstat_seq_open(struct inode *inode, struct file *file) return single_open_net(inode, file, sockstat_seq_show); } -static int sockstat_seq_release(struct inode *inode, struct file *file) -{ - struct net *net = ((struct seq_file *)file->private_data)->private; - - put_net(net); - return single_release(inode, file); -} - static const struct file_operations sockstat_seq_fops = { .owner = THIS_MODULE, .open = sockstat_seq_open, .read = seq_read, .llseek = seq_lseek, - .release = sockstat_seq_release, + .release = single_release_net, }; /* snmp items */ @@ -383,20 +375,12 @@ static int snmp_seq_open(struct inode *inode, struct file *file) return single_open_net(inode, file, snmp_seq_show); } -static int snmp_seq_release(struct inode *inode, struct file *file) -{ - struct net *net = ((struct seq_file *)file->private_data)->private; - - put_net(net); - return single_release(inode, file); -} - static const struct file_operations snmp_seq_fops = { .owner = THIS_MODULE, .open = snmp_seq_open, .read = seq_read, .llseek = seq_lseek, - .release = snmp_seq_release, + .release = single_release_net, }; @@ -438,20 +422,12 @@ static int netstat_seq_open(struct inode *inode, struct file *file) return single_open_net(inode, file, netstat_seq_show); } -static int netstat_seq_release(struct inode *inode, struct file *file) -{ - struct net *net = ((struct seq_file *)file->private_data)->private; - - put_net(net); - return single_release(inode, file); -} - static const struct file_operations netstat_seq_fops = { .owner = THIS_MODULE, .open = netstat_seq_open, .read = seq_read, .llseek = seq_lseek, - .release = netstat_seq_release, + .release = single_release_net, }; static __net_init int ip_proc_init_net(struct net *net) diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c index 29c5a79444c2..70940b3654a1 100644 --- a/net/ipv6/proc.c +++ b/net/ipv6/proc.c @@ -186,20 +186,12 @@ static int sockstat6_seq_open(struct inode *inode, struct file *file) return single_open_net(inode, file, sockstat6_seq_show); } -static int sockstat6_seq_release(struct inode *inode, struct file *file) -{ - struct net *net = ((struct seq_file *)file->private_data)->private; - - put_net(net); - return single_release(inode, file); -} - static const struct file_operations sockstat6_seq_fops = { .owner = THIS_MODULE, .open = sockstat6_seq_open, .read = seq_read, .llseek = seq_lseek, - .release = sockstat6_seq_release, + .release = single_release_net, }; static int snmp6_seq_open(struct inode *inode, struct file *file) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index fb7ff8f0c6db..cb8a51271b67 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2419,20 +2419,12 @@ static int ipv6_route_open(struct inode *inode, struct file *file) return single_open_net(inode, file, ipv6_route_show); } -static int ipv6_route_release(struct inode *inode, struct file *file) -{ - struct seq_file *seq = file->private_data; - struct net *net = seq->private; - put_net(net); - return single_release(inode, file); -} - static const struct file_operations ipv6_route_proc_fops = { .owner = THIS_MODULE, .open = ipv6_route_open, .read = seq_read, .llseek = seq_lseek, - .release = ipv6_route_release, + .release = single_release_net, }; static int rt6_stats_seq_show(struct seq_file *seq, void *v) @@ -2455,20 +2447,12 @@ static int rt6_stats_seq_open(struct inode *inode, struct file *file) return single_open_net(inode, file, rt6_stats_seq_show); } -static int rt6_stats_seq_release(struct inode *inode, struct file *file) -{ - struct seq_file *seq = file->private_data; - struct net *net = (struct net *)seq->private; - put_net(net); - return single_release(inode, file); -} - static const struct file_operations rt6_stats_seq_fops = { .owner = THIS_MODULE, .open = rt6_stats_seq_open, .read = seq_read, .llseek = seq_lseek, - .release = rt6_stats_seq_release, + .release = single_release_net, }; #endif /* CONFIG_PROC_FS */ -- cgit v1.2.3 From ae79cdaacb5599781f8bb49f4bdd5723029669cf Mon Sep 17 00:00:00 2001 From: "venkatesh.pallipadi@intel.com" Date: Fri, 18 Jul 2008 16:08:13 -0700 Subject: x86: Add a arch directory for x86 under debugfs Add a directory for x86 arch under debugfs. Can be used to accumulate all x86 specific debugfs files. Signed-off-by: Venkatesh Pallipadi Signed-off-by: H. Peter Anvin --- arch/x86/kernel/kdebugfs.c | 8 ++++++++ include/linux/debugfs.h | 2 ++ 2 files changed, 10 insertions(+) (limited to 'include/linux') diff --git a/arch/x86/kernel/kdebugfs.c b/arch/x86/kernel/kdebugfs.c index c03205991718..f2d43bc75514 100644 --- a/arch/x86/kernel/kdebugfs.c +++ b/arch/x86/kernel/kdebugfs.c @@ -12,9 +12,13 @@ #include #include #include +#include #include +struct dentry *arch_debugfs_dir; +EXPORT_SYMBOL(arch_debugfs_dir); + #ifdef CONFIG_DEBUG_BOOT_PARAMS struct setup_data_node { u64 paddr; @@ -209,6 +213,10 @@ static int __init arch_kdebugfs_init(void) { int error = 0; + arch_debugfs_dir = debugfs_create_dir("x86", NULL); + if (!arch_debugfs_dir) + return -ENOMEM; + #ifdef CONFIG_DEBUG_BOOT_PARAMS error = boot_params_kdebugfs_init(); #endif diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index 7266124361b4..32755cdf68db 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -26,6 +26,8 @@ struct debugfs_blob_wrapper { unsigned long size; }; +extern struct dentry *arch_debugfs_dir; + #if defined(CONFIG_DEBUG_FS) /* declared over in file.c */ -- cgit v1.2.3 From 8913336a7e8d56e984109a3137d6c0e3362596a4 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Fri, 18 Jul 2008 18:05:19 -0700 Subject: packet: add PACKET_RESERVE sockopt Add new sockopt to reserve some headroom in the mmaped ring frames in front of the packet payload. This can be used f.i. when the VLAN header needs to be (re)constructed to avoid moving the entire payload. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/if_packet.h | 1 + net/packet/af_packet.c | 29 ++++++++++++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/if_packet.h b/include/linux/if_packet.h index a630295b255f..18db0668065a 100644 --- a/include/linux/if_packet.h +++ b/include/linux/if_packet.h @@ -45,6 +45,7 @@ struct sockaddr_ll #define PACKET_ORIGDEV 9 #define PACKET_VERSION 10 #define PACKET_HDRLEN 11 +#define PACKET_RESERVE 12 struct tpacket_stats { diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index db792e02a37f..de73bcb5235a 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -188,6 +188,7 @@ struct packet_sock { unsigned int pg_vec_len; enum tpacket_versions tp_version; unsigned int tp_hdrlen; + unsigned int tp_reserve; #endif }; @@ -635,11 +636,13 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, struct packe snaplen = res; if (sk->sk_type == SOCK_DGRAM) { - macoff = netoff = TPACKET_ALIGN(po->tp_hdrlen) + 16; + macoff = netoff = TPACKET_ALIGN(po->tp_hdrlen) + 16 + + po->tp_reserve; } else { unsigned maclen = skb_network_offset(skb); netoff = TPACKET_ALIGN(po->tp_hdrlen + - (maclen < 16 ? 16 : maclen)); + (maclen < 16 ? 16 : maclen)) + + po->tp_reserve; macoff = netoff - maclen; } @@ -1448,6 +1451,19 @@ packet_setsockopt(struct socket *sock, int level, int optname, char __user *optv return -EINVAL; } } + case PACKET_RESERVE: + { + unsigned int val; + + if (optlen != sizeof(val)) + return -EINVAL; + if (po->pg_vec) + return -EBUSY; + if (copy_from_user(&val, optval, sizeof(val))) + return -EFAULT; + po->tp_reserve = val; + return 0; + } #endif case PACKET_AUXDATA: { @@ -1547,6 +1563,12 @@ static int packet_getsockopt(struct socket *sock, int level, int optname, } data = &val; break; + case PACKET_RESERVE: + if (len > sizeof(unsigned int)) + len = sizeof(unsigned int); + val = po->tp_reserve; + data = &val; + break; #endif default: return -ENOPROTOOPT; @@ -1790,7 +1812,8 @@ static int packet_set_ring(struct sock *sk, struct tpacket_req *req, int closing return -EINVAL; if (unlikely(req->tp_block_size & (PAGE_SIZE - 1))) return -EINVAL; - if (unlikely(req->tp_frame_size < po->tp_hdrlen)) + if (unlikely(req->tp_frame_size < po->tp_hdrlen + + po->tp_reserve)) return -EINVAL; if (unlikely(req->tp_frame_size & (TPACKET_ALIGNMENT - 1))) return -EINVAL; -- cgit v1.2.3 From 5ec461d083066441c1a280aa9e13d3bb5c3e008a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sat, 19 Jul 2008 00:40:25 -0400 Subject: Input: add microphone insert switch definition Add a new switch type to the input API for reporting microphone insertion. This will be used by the ALSA jack reporting API. Signed-off-by: Mark Brown Signed-off-by: Dmitry Torokhov --- include/linux/input.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/input.h b/include/linux/input.h index 3e86b61d2e4c..1be28cbbc57b 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -642,6 +642,7 @@ struct input_absinfo { #define SW_RFKILL_ALL 0x03 /* rfkill master switch, type "any" set = radio enabled */ #define SW_RADIO SW_RFKILL_ALL /* deprecated */ +#define SW_MICROPHONE_INSERT 0x04 /* set = inserted */ #define SW_MAX 0x0f #define SW_CNT (SW_MAX+1) -- cgit v1.2.3 From 92c49890922d54cba4b1eadeb0b185773c2c9570 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Sat, 19 Jul 2008 00:43:54 -0400 Subject: Input: add switch for dock events Add a SW_DOCK switch to input.h. ACPI docks currently send their docking status as a uevent, but not all docks are ACPI or correspond to a device. In that case, it makes more sense to simply generate an input event on docking or undocking. Signed-off-by: Matthew Garrett Signed-off-by: Andrew Morton Signed-off-by: Dmitry Torokhov --- include/linux/input.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/input.h b/include/linux/input.h index 1be28cbbc57b..feaea76f0ef3 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -643,6 +643,7 @@ struct input_absinfo { set = radio enabled */ #define SW_RADIO SW_RFKILL_ALL /* deprecated */ #define SW_MICROPHONE_INSERT 0x04 /* set = inserted */ +#define SW_DOCK 0x05 /* set = plugged into dock */ #define SW_MAX 0x0f #define SW_CNT (SW_MAX+1) -- cgit v1.2.3 From 3072367300aa8c779e3a14ee8e89de079e90f3ad Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 18 Jul 2008 22:50:15 -0700 Subject: pkt_sched: Manage qdisc list inside of root qdisc. Idea is from Patrick McHardy. Instead of managing the list of qdiscs on the device level, manage it in the root qdisc of a netdev_queue. This solves all kinds of visibility issues during qdisc destruction. The way to iterate over all qdiscs of a netdev_queue is to visit the netdev_queue->qdisc, and then traverse it's list. The only special case is to ignore builting qdiscs at the root when dumping or doing a qdisc_lookup(). That was not needed previously because builtin qdiscs were not added to the device's qdisc_list. Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 - net/core/dev.c | 2 - net/sched/sch_api.c | 175 ++++++++++++++++++++++++++++++++++------------ net/sched/sch_generic.c | 10 +-- 4 files changed, 133 insertions(+), 56 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 9c5a68850114..812bcd8b4363 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -636,8 +636,6 @@ struct net_device unsigned int real_num_tx_queues; unsigned long tx_queue_len; /* Max frames per queue allowed */ - spinlock_t qdisc_list_lock; - struct list_head qdisc_list; /* * One part is mostly used on xmit path (device) diff --git a/net/core/dev.c b/net/core/dev.c index e54acde839da..065b9817e209 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3888,8 +3888,6 @@ int register_netdevice(struct net_device *dev) net = dev_net(dev); spin_lock_init(&dev->addr_list_lock); - spin_lock_init(&dev->qdisc_list_lock); - INIT_LIST_HEAD(&dev->qdisc_list); netdev_init_queue_locks(dev); dev->iflink = -1; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index b3ef8307204e..fb43731c9860 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -185,11 +185,20 @@ EXPORT_SYMBOL(unregister_qdisc); struct Qdisc *qdisc_lookup(struct net_device *dev, u32 handle) { - struct Qdisc *q; + unsigned int i; + + for (i = 0; i < dev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(dev, i); + struct Qdisc *q, *txq_root = txq->qdisc; - list_for_each_entry(q, &dev->qdisc_list, list) { - if (q->handle == handle) - return q; + if (!(txq_root->flags & TCQ_F_BUILTIN) && + txq_root->handle == handle) + return txq_root; + + list_for_each_entry(q, &txq_root->list, list) { + if (q->handle == handle) + return q; + } } return NULL; } @@ -676,9 +685,8 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue, goto err_out3; } } - spin_lock_bh(&dev->qdisc_list_lock); - list_add_tail(&sch->list, &dev->qdisc_list); - spin_unlock_bh(&dev->qdisc_list_lock); + if (parent) + list_add_tail(&sch->list, &dev_queue->qdisc->list); return sch; } @@ -1037,13 +1045,57 @@ err_out: return -EINVAL; } +static bool tc_qdisc_dump_ignore(struct Qdisc *q) +{ + return (q->flags & TCQ_F_BUILTIN) ? true : false; +} + +static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb, + struct netlink_callback *cb, + int *q_idx_p, int s_q_idx) +{ + int ret = 0, q_idx = *q_idx_p; + struct Qdisc *q; + + if (!root) + return 0; + + q = root; + if (q_idx < s_q_idx) { + q_idx++; + } else { + if (!tc_qdisc_dump_ignore(q) && + tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWQDISC) <= 0) + goto done; + q_idx++; + } + list_for_each_entry(q, &root->list, list) { + if (q_idx < s_q_idx) { + q_idx++; + continue; + } + if (!tc_qdisc_dump_ignore(q) && + tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWQDISC) <= 0) + goto done; + q_idx++; + } + +out: + *q_idx_p = q_idx; + return ret; +done: + ret = -1; + goto out; +} + static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb) { struct net *net = sock_net(skb->sk); int idx, q_idx; int s_idx, s_q_idx; struct net_device *dev; - struct Qdisc *q; if (net != &init_net) return 0; @@ -1053,21 +1105,22 @@ static int tc_dump_qdisc(struct sk_buff *skb, struct netlink_callback *cb) read_lock(&dev_base_lock); idx = 0; for_each_netdev(&init_net, dev) { + struct netdev_queue *dev_queue; + if (idx < s_idx) goto cont; if (idx > s_idx) s_q_idx = 0; q_idx = 0; - list_for_each_entry(q, &dev->qdisc_list, list) { - if (q_idx < s_q_idx) { - q_idx++; - continue; - } - if (tc_fill_qdisc(skb, q, q->parent, NETLINK_CB(cb->skb).pid, - cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWQDISC) <= 0) - goto done; - q_idx++; - } + + dev_queue = netdev_get_tx_queue(dev, 0); + if (tc_dump_qdisc_root(dev_queue->qdisc, skb, cb, &q_idx, s_q_idx) < 0) + goto done; + + dev_queue = &dev->rx_queue; + if (tc_dump_qdisc_root(dev_queue->qdisc, skb, cb, &q_idx, s_q_idx) < 0) + goto done; + cont: idx++; } @@ -1285,15 +1338,62 @@ static int qdisc_class_dump(struct Qdisc *q, unsigned long cl, struct qdisc_walk a->cb->nlh->nlmsg_seq, NLM_F_MULTI, RTM_NEWTCLASS); } +static int tc_dump_tclass_qdisc(struct Qdisc *q, struct sk_buff *skb, + struct tcmsg *tcm, struct netlink_callback *cb, + int *t_p, int s_t) +{ + struct qdisc_dump_args arg; + + if (tc_qdisc_dump_ignore(q) || + *t_p < s_t || !q->ops->cl_ops || + (tcm->tcm_parent && + TC_H_MAJ(tcm->tcm_parent) != q->handle)) { + (*t_p)++; + return 0; + } + if (*t_p > s_t) + memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(cb->args[0])); + arg.w.fn = qdisc_class_dump; + arg.skb = skb; + arg.cb = cb; + arg.w.stop = 0; + arg.w.skip = cb->args[1]; + arg.w.count = 0; + q->ops->cl_ops->walk(q, &arg.w); + cb->args[1] = arg.w.count; + if (arg.w.stop) + return -1; + (*t_p)++; + return 0; +} + +static int tc_dump_tclass_root(struct Qdisc *root, struct sk_buff *skb, + struct tcmsg *tcm, struct netlink_callback *cb, + int *t_p, int s_t) +{ + struct Qdisc *q; + + if (!root) + return 0; + + if (tc_dump_tclass_qdisc(root, skb, tcm, cb, t_p, s_t) < 0) + return -1; + + list_for_each_entry(q, &root->list, list) { + if (tc_dump_tclass_qdisc(q, skb, tcm, cb, t_p, s_t) < 0) + return -1; + } + + return 0; +} + static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb) { + struct tcmsg *tcm = (struct tcmsg*)NLMSG_DATA(cb->nlh); struct net *net = sock_net(skb->sk); - int t; - int s_t; + struct netdev_queue *dev_queue; struct net_device *dev; - struct Qdisc *q; - struct tcmsg *tcm = (struct tcmsg*)NLMSG_DATA(cb->nlh); - struct qdisc_dump_args arg; + int t, s_t; if (net != &init_net) return 0; @@ -1306,28 +1406,15 @@ static int tc_dump_tclass(struct sk_buff *skb, struct netlink_callback *cb) s_t = cb->args[0]; t = 0; - list_for_each_entry(q, &dev->qdisc_list, list) { - if (t < s_t || !q->ops->cl_ops || - (tcm->tcm_parent && - TC_H_MAJ(tcm->tcm_parent) != q->handle)) { - t++; - continue; - } - if (t > s_t) - memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(cb->args[0])); - arg.w.fn = qdisc_class_dump; - arg.skb = skb; - arg.cb = cb; - arg.w.stop = 0; - arg.w.skip = cb->args[1]; - arg.w.count = 0; - q->ops->cl_ops->walk(q, &arg.w); - cb->args[1] = arg.w.count; - if (arg.w.stop) - break; - t++; - } + dev_queue = netdev_get_tx_queue(dev, 0); + if (tc_dump_tclass_root(dev_queue->qdisc, skb, tcm, cb, &t, s_t) < 0) + goto done; + + dev_queue = &dev->rx_queue; + if (tc_dump_tclass_root(dev_queue->qdisc, skb, tcm, cb, &t, s_t) < 0) + goto done; +done: cb->args[0] = t; dev_put(dev); diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index e244c462e6bd..14cc443d0490 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -480,15 +480,12 @@ static void __qdisc_destroy(struct rcu_head *head) void qdisc_destroy(struct Qdisc *qdisc) { - struct net_device *dev = qdisc_dev(qdisc); - if (qdisc->flags & TCQ_F_BUILTIN || !atomic_dec_and_test(&qdisc->refcnt)) return; - spin_lock_bh(&dev->qdisc_list_lock); - list_del(&qdisc->list); - spin_unlock_bh(&dev->qdisc_list_lock); + if (qdisc->parent) + list_del(&qdisc->list); call_rcu(&qdisc->q_rcu, __qdisc_destroy); } @@ -520,9 +517,6 @@ static void attach_one_default_qdisc(struct net_device *dev, printk(KERN_INFO "%s: activation failed\n", dev->name); return; } - spin_lock_bh(&dev->qdisc_list_lock); - list_add_tail(&qdisc->list, &dev->qdisc_list); - spin_unlock_bh(&dev->qdisc_list_lock); } else { qdisc = &noqueue_qdisc; } -- cgit v1.2.3 From 4389dded7767d24290463f2a8302ba3253ebdd56 Mon Sep 17 00:00:00 2001 From: Adam Langley Date: Sat, 19 Jul 2008 00:07:02 -0700 Subject: tcp: Remove redundant checks when setting eff_sacks Remove redundant checks when setting eff_sacks and make the number of SACKs a compile time constant. Now that the options code knows how many SACK blocks can fit in the header, we don't need to have the SACK code guessing at it. Signed-off-by: Adam Langley Signed-off-by: David S. Miller --- include/linux/tcp.h | 6 ++++++ net/ipv4/tcp_input.c | 25 ++++++++++--------------- 2 files changed, 16 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 07e79bdb9cdf..2e2557388e36 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -224,6 +224,12 @@ struct tcp_options_received { u16 mss_clamp; /* Maximal mss, negotiated at connection setup */ }; +/* This is the max number of SACKS that we'll generate and process. It's safe + * to increse this, although since: + * size = TCPOLEN_SACK_BASE_ALIGNED (4) + n * TCPOLEN_SACK_PERBLOCK (8) + * only four options will fit in a standard TCP header */ +#define TCP_NUM_SACKS 4 + struct tcp_request_sock { struct inet_request_sock req; #ifdef CONFIG_TCP_MD5SIG diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 88810bc01370..1f5e6049883e 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -1423,10 +1423,10 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb, unsigned char *ptr = (skb_transport_header(ack_skb) + TCP_SKB_CB(ack_skb)->sacked); struct tcp_sack_block_wire *sp_wire = (struct tcp_sack_block_wire *)(ptr+2); - struct tcp_sack_block sp[4]; + struct tcp_sack_block sp[TCP_NUM_SACKS]; struct tcp_sack_block *cache; struct sk_buff *skb; - int num_sacks = (ptr[1] - TCPOLEN_SACK_BASE) >> 3; + int num_sacks = min(TCP_NUM_SACKS, (ptr[1] - TCPOLEN_SACK_BASE) >> 3); int used_sacks; int reord = tp->packets_out; int flag = 0; @@ -3735,8 +3735,7 @@ static void tcp_dsack_set(struct sock *sk, u32 seq, u32 end_seq) tp->rx_opt.dsack = 1; tp->duplicate_sack[0].start_seq = seq; tp->duplicate_sack[0].end_seq = end_seq; - tp->rx_opt.eff_sacks = min(tp->rx_opt.num_sacks + 1, - 4 - tp->rx_opt.tstamp_ok); + tp->rx_opt.eff_sacks = tp->rx_opt.num_sacks + 1; } } @@ -3791,9 +3790,8 @@ static void tcp_sack_maybe_coalesce(struct tcp_sock *tp) * Decrease num_sacks. */ tp->rx_opt.num_sacks--; - tp->rx_opt.eff_sacks = min(tp->rx_opt.num_sacks + - tp->rx_opt.dsack, - 4 - tp->rx_opt.tstamp_ok); + tp->rx_opt.eff_sacks = tp->rx_opt.num_sacks + + tp->rx_opt.dsack; for (i = this_sack; i < tp->rx_opt.num_sacks; i++) sp[i] = sp[i + 1]; continue; @@ -3843,7 +3841,7 @@ static void tcp_sack_new_ofo_skb(struct sock *sk, u32 seq, u32 end_seq) * * If the sack array is full, forget about the last one. */ - if (this_sack >= 4) { + if (this_sack >= TCP_NUM_SACKS) { this_sack--; tp->rx_opt.num_sacks--; sp--; @@ -3856,8 +3854,7 @@ new_sack: sp->start_seq = seq; sp->end_seq = end_seq; tp->rx_opt.num_sacks++; - tp->rx_opt.eff_sacks = min(tp->rx_opt.num_sacks + tp->rx_opt.dsack, - 4 - tp->rx_opt.tstamp_ok); + tp->rx_opt.eff_sacks = tp->rx_opt.num_sacks + tp->rx_opt.dsack; } /* RCV.NXT advances, some SACKs should be eaten. */ @@ -3894,9 +3891,8 @@ static void tcp_sack_remove(struct tcp_sock *tp) } if (num_sacks != tp->rx_opt.num_sacks) { tp->rx_opt.num_sacks = num_sacks; - tp->rx_opt.eff_sacks = min(tp->rx_opt.num_sacks + - tp->rx_opt.dsack, - 4 - tp->rx_opt.tstamp_ok); + tp->rx_opt.eff_sacks = tp->rx_opt.num_sacks + + tp->rx_opt.dsack; } } @@ -3975,8 +3971,7 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) if (tp->rx_opt.dsack) { tp->rx_opt.dsack = 0; - tp->rx_opt.eff_sacks = min_t(unsigned int, tp->rx_opt.num_sacks, - 4 - tp->rx_opt.tstamp_ok); + tp->rx_opt.eff_sacks = tp->rx_opt.num_sacks; } /* Queue data for delivery to the user. -- cgit v1.2.3 From 230b183921ecbaa5fedc0d35ad6ba7bb64b6e06a Mon Sep 17 00:00:00 2001 From: YOSHIFUJI Hideaki Date: Sat, 19 Jul 2008 22:35:47 -0700 Subject: net: Use standard structures for generic socket address structures. Use sockaddr_storage{} for generic socket address storage and ensures proper alignment. Use sockaddr{} for pointers to omit several casts. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller --- include/linux/socket.h | 6 ++-- include/net/compat.h | 2 +- net/compat.c | 2 +- net/core/iovec.c | 2 +- net/socket.c | 82 ++++++++++++++++++++++++++++---------------------- 5 files changed, 52 insertions(+), 42 deletions(-) (limited to 'include/linux') diff --git a/include/linux/socket.h b/include/linux/socket.h index bd2b30a74e76..950af631e7fb 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -306,10 +306,10 @@ extern int csum_partial_copy_fromiovecend(unsigned char *kdata, int offset, unsigned int len, __wsum *csump); -extern int verify_iovec(struct msghdr *m, struct iovec *iov, char *address, int mode); +extern int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr *address, int mode); extern int memcpy_toiovec(struct iovec *v, unsigned char *kdata, int len); -extern int move_addr_to_user(void *kaddr, int klen, void __user *uaddr, int __user *ulen); -extern int move_addr_to_kernel(void __user *uaddr, int ulen, void *kaddr); +extern int move_addr_to_user(struct sockaddr *kaddr, int klen, void __user *uaddr, int __user *ulen); +extern int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr *kaddr); extern int put_cmsg(struct msghdr*, int level, int type, int len, void *data); #endif diff --git a/include/net/compat.h b/include/net/compat.h index 164cb682e220..5bbf8bf9efea 100644 --- a/include/net/compat.h +++ b/include/net/compat.h @@ -32,7 +32,7 @@ extern int compat_sock_get_timestampns(struct sock *, struct timespec __user *); #endif /* defined(CONFIG_COMPAT) */ extern int get_compat_msghdr(struct msghdr *, struct compat_msghdr __user *); -extern int verify_compat_iovec(struct msghdr *, struct iovec *, char *, int); +extern int verify_compat_iovec(struct msghdr *, struct iovec *, struct sockaddr *, int); extern asmlinkage long compat_sys_sendmsg(int,struct compat_msghdr __user *,unsigned); extern asmlinkage long compat_sys_recvmsg(int,struct compat_msghdr __user *,unsigned); extern asmlinkage long compat_sys_getsockopt(int, int, int, char __user *, int __user *); diff --git a/net/compat.c b/net/compat.c index c823f6f290cb..6e1b03b51933 100644 --- a/net/compat.c +++ b/net/compat.c @@ -75,7 +75,7 @@ int get_compat_msghdr(struct msghdr *kmsg, struct compat_msghdr __user *umsg) /* I've named the args so it is easy to tell whose space the pointers are in. */ int verify_compat_iovec(struct msghdr *kern_msg, struct iovec *kern_iov, - char *kern_address, int mode) + struct sockaddr *kern_address, int mode) { int tot_len; diff --git a/net/core/iovec.c b/net/core/iovec.c index 755c37fdaee7..4c9c0121c9da 100644 --- a/net/core/iovec.c +++ b/net/core/iovec.c @@ -36,7 +36,7 @@ * in any case. */ -int verify_iovec(struct msghdr *m, struct iovec *iov, char *address, int mode) +int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr *address, int mode) { int size, err, ct; diff --git a/net/socket.c b/net/socket.c index 81fe82513046..1ba57d888981 100644 --- a/net/socket.c +++ b/net/socket.c @@ -180,9 +180,9 @@ static DEFINE_PER_CPU(int, sockets_in_use) = 0; * invalid addresses -EFAULT is returned. On a success 0 is returned. */ -int move_addr_to_kernel(void __user *uaddr, int ulen, void *kaddr) +int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr *kaddr) { - if (ulen < 0 || ulen > MAX_SOCK_ADDR) + if (ulen < 0 || ulen > sizeof(struct sockaddr_storage)) return -EINVAL; if (ulen == 0) return 0; @@ -208,7 +208,7 @@ int move_addr_to_kernel(void __user *uaddr, int ulen, void *kaddr) * specified. Zero is returned for a success. */ -int move_addr_to_user(void *kaddr, int klen, void __user *uaddr, +int move_addr_to_user(struct sockaddr *kaddr, int klen, void __user *uaddr, int __user *ulen) { int err; @@ -219,7 +219,7 @@ int move_addr_to_user(void *kaddr, int klen, void __user *uaddr, return err; if (len > klen) len = klen; - if (len < 0 || len > MAX_SOCK_ADDR) + if (len < 0 || len > sizeof(struct sockaddr_storage)) return -EINVAL; if (len) { if (audit_sockaddr(klen, kaddr)) @@ -1342,20 +1342,20 @@ out_fd: asmlinkage long sys_bind(int fd, struct sockaddr __user *umyaddr, int addrlen) { struct socket *sock; - char address[MAX_SOCK_ADDR]; + struct sockaddr_storage address; int err, fput_needed; sock = sockfd_lookup_light(fd, &err, &fput_needed); if (sock) { - err = move_addr_to_kernel(umyaddr, addrlen, address); + err = move_addr_to_kernel(umyaddr, addrlen, (struct sockaddr *)&address); if (err >= 0) { err = security_socket_bind(sock, - (struct sockaddr *)address, + (struct sockaddr *)&address, addrlen); if (!err) err = sock->ops->bind(sock, (struct sockaddr *) - address, addrlen); + &address, addrlen); } fput_light(sock->file, fput_needed); } @@ -1407,7 +1407,7 @@ asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, struct socket *sock, *newsock; struct file *newfile; int err, len, newfd, fput_needed; - char address[MAX_SOCK_ADDR]; + struct sockaddr_storage address; sock = sockfd_lookup_light(fd, &err, &fput_needed); if (!sock) @@ -1446,13 +1446,13 @@ asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, goto out_fd; if (upeer_sockaddr) { - if (newsock->ops->getname(newsock, (struct sockaddr *)address, + if (newsock->ops->getname(newsock, (struct sockaddr *)&address, &len, 2) < 0) { err = -ECONNABORTED; goto out_fd; } - err = move_addr_to_user(address, len, upeer_sockaddr, - upeer_addrlen); + err = move_addr_to_user((struct sockaddr *)&address, + len, upeer_sockaddr, upeer_addrlen); if (err < 0) goto out_fd; } @@ -1495,22 +1495,22 @@ asmlinkage long sys_connect(int fd, struct sockaddr __user *uservaddr, int addrlen) { struct socket *sock; - char address[MAX_SOCK_ADDR]; + struct sockaddr_storage address; int err, fput_needed; sock = sockfd_lookup_light(fd, &err, &fput_needed); if (!sock) goto out; - err = move_addr_to_kernel(uservaddr, addrlen, address); + err = move_addr_to_kernel(uservaddr, addrlen, (struct sockaddr *)&address); if (err < 0) goto out_put; err = - security_socket_connect(sock, (struct sockaddr *)address, addrlen); + security_socket_connect(sock, (struct sockaddr *)&address, addrlen); if (err) goto out_put; - err = sock->ops->connect(sock, (struct sockaddr *)address, addrlen, + err = sock->ops->connect(sock, (struct sockaddr *)&address, addrlen, sock->file->f_flags); out_put: fput_light(sock->file, fput_needed); @@ -1527,7 +1527,7 @@ asmlinkage long sys_getsockname(int fd, struct sockaddr __user *usockaddr, int __user *usockaddr_len) { struct socket *sock; - char address[MAX_SOCK_ADDR]; + struct sockaddr_storage address; int len, err, fput_needed; sock = sockfd_lookup_light(fd, &err, &fput_needed); @@ -1538,10 +1538,10 @@ asmlinkage long sys_getsockname(int fd, struct sockaddr __user *usockaddr, if (err) goto out_put; - err = sock->ops->getname(sock, (struct sockaddr *)address, &len, 0); + err = sock->ops->getname(sock, (struct sockaddr *)&address, &len, 0); if (err) goto out_put; - err = move_addr_to_user(address, len, usockaddr, usockaddr_len); + err = move_addr_to_user((struct sockaddr *)&address, len, usockaddr, usockaddr_len); out_put: fput_light(sock->file, fput_needed); @@ -1558,7 +1558,7 @@ asmlinkage long sys_getpeername(int fd, struct sockaddr __user *usockaddr, int __user *usockaddr_len) { struct socket *sock; - char address[MAX_SOCK_ADDR]; + struct sockaddr_storage address; int len, err, fput_needed; sock = sockfd_lookup_light(fd, &err, &fput_needed); @@ -1570,10 +1570,10 @@ asmlinkage long sys_getpeername(int fd, struct sockaddr __user *usockaddr, } err = - sock->ops->getname(sock, (struct sockaddr *)address, &len, + sock->ops->getname(sock, (struct sockaddr *)&address, &len, 1); if (!err) - err = move_addr_to_user(address, len, usockaddr, + err = move_addr_to_user((struct sockaddr *)&address, len, usockaddr, usockaddr_len); fput_light(sock->file, fput_needed); } @@ -1591,7 +1591,7 @@ asmlinkage long sys_sendto(int fd, void __user *buff, size_t len, int addr_len) { struct socket *sock; - char address[MAX_SOCK_ADDR]; + struct sockaddr_storage address; int err; struct msghdr msg; struct iovec iov; @@ -1610,10 +1610,10 @@ asmlinkage long sys_sendto(int fd, void __user *buff, size_t len, msg.msg_controllen = 0; msg.msg_namelen = 0; if (addr) { - err = move_addr_to_kernel(addr, addr_len, address); + err = move_addr_to_kernel(addr, addr_len, (struct sockaddr *)&address); if (err < 0) goto out_put; - msg.msg_name = address; + msg.msg_name = (struct sockaddr *)&address; msg.msg_namelen = addr_len; } if (sock->file->f_flags & O_NONBLOCK) @@ -1649,7 +1649,7 @@ asmlinkage long sys_recvfrom(int fd, void __user *ubuf, size_t size, struct socket *sock; struct iovec iov; struct msghdr msg; - char address[MAX_SOCK_ADDR]; + struct sockaddr_storage address; int err, err2; int fput_needed; @@ -1663,14 +1663,15 @@ asmlinkage long sys_recvfrom(int fd, void __user *ubuf, size_t size, msg.msg_iov = &iov; iov.iov_len = size; iov.iov_base = ubuf; - msg.msg_name = address; - msg.msg_namelen = MAX_SOCK_ADDR; + msg.msg_name = (struct sockaddr *)&address; + msg.msg_namelen = sizeof(address); if (sock->file->f_flags & O_NONBLOCK) flags |= MSG_DONTWAIT; err = sock_recvmsg(sock, &msg, size, flags); if (err >= 0 && addr != NULL) { - err2 = move_addr_to_user(address, msg.msg_namelen, addr, addr_len); + err2 = move_addr_to_user((struct sockaddr *)&address, + msg.msg_namelen, addr, addr_len); if (err2 < 0) err = err2; } @@ -1790,7 +1791,7 @@ asmlinkage long sys_sendmsg(int fd, struct msghdr __user *msg, unsigned flags) struct compat_msghdr __user *msg_compat = (struct compat_msghdr __user *)msg; struct socket *sock; - char address[MAX_SOCK_ADDR]; + struct sockaddr_storage address; struct iovec iovstack[UIO_FASTIOV], *iov = iovstack; unsigned char ctl[sizeof(struct cmsghdr) + 20] __attribute__ ((aligned(sizeof(__kernel_size_t)))); @@ -1828,9 +1829,13 @@ asmlinkage long sys_sendmsg(int fd, struct msghdr __user *msg, unsigned flags) /* This will also move the address data into kernel space */ if (MSG_CMSG_COMPAT & flags) { - err = verify_compat_iovec(&msg_sys, iov, address, VERIFY_READ); + err = verify_compat_iovec(&msg_sys, iov, + (struct sockaddr *)&address, + VERIFY_READ); } else - err = verify_iovec(&msg_sys, iov, address, VERIFY_READ); + err = verify_iovec(&msg_sys, iov, + (struct sockaddr *)&address, + VERIFY_READ); if (err < 0) goto out_freeiov; total_len = err; @@ -1901,7 +1906,7 @@ asmlinkage long sys_recvmsg(int fd, struct msghdr __user *msg, int fput_needed; /* kernel mode address */ - char addr[MAX_SOCK_ADDR]; + struct sockaddr_storage addr; /* user mode address pointers */ struct sockaddr __user *uaddr; @@ -1939,9 +1944,13 @@ asmlinkage long sys_recvmsg(int fd, struct msghdr __user *msg, uaddr = (__force void __user *)msg_sys.msg_name; uaddr_len = COMPAT_NAMELEN(msg); if (MSG_CMSG_COMPAT & flags) { - err = verify_compat_iovec(&msg_sys, iov, addr, VERIFY_WRITE); + err = verify_compat_iovec(&msg_sys, iov, + (struct sockaddr *)&addr, + VERIFY_WRITE); } else - err = verify_iovec(&msg_sys, iov, addr, VERIFY_WRITE); + err = verify_iovec(&msg_sys, iov, + (struct sockaddr *)&addr, + VERIFY_WRITE); if (err < 0) goto out_freeiov; total_len = err; @@ -1957,7 +1966,8 @@ asmlinkage long sys_recvmsg(int fd, struct msghdr __user *msg, len = err; if (uaddr != NULL) { - err = move_addr_to_user(addr, msg_sys.msg_namelen, uaddr, + err = move_addr_to_user((struct sockaddr *)&addr, + msg_sys.msg_namelen, uaddr, uaddr_len); if (err < 0) goto out_freeiov; -- cgit v1.2.3 From 175f9c1bba9b825d22b142d183c9e175488b260c Mon Sep 17 00:00:00 2001 From: Jussi Kivilinna Date: Sun, 20 Jul 2008 00:08:47 -0700 Subject: net_sched: Add size table for qdiscs Add size table functions for qdiscs and calculate packet size in qdisc_enqueue(). Based on patch by Patrick McHardy http://marc.info/?l=linux-netdev&m=115201979221729&w=2 Signed-off-by: Jussi Kivilinna Signed-off-by: David S. Miller --- include/linux/pkt_sched.h | 20 ++++++ include/linux/rtnetlink.h | 1 + include/net/pkt_sched.h | 1 + include/net/sch_generic.h | 25 +++++++- net/sched/sch_api.c | 151 +++++++++++++++++++++++++++++++++++++++++++++- net/sched/sch_generic.c | 1 + net/sched/sch_netem.c | 5 +- 7 files changed, 199 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h index 87f4e0fa8f27..e5de421ac7b4 100644 --- a/include/linux/pkt_sched.h +++ b/include/linux/pkt_sched.h @@ -85,6 +85,26 @@ struct tc_ratespec #define TC_RTAB_SIZE 1024 +struct tc_sizespec { + unsigned char cell_log; + unsigned char size_log; + short cell_align; + int overhead; + unsigned int linklayer; + unsigned int mpu; + unsigned int mtu; + unsigned int tsize; +}; + +enum { + TCA_STAB_UNSPEC, + TCA_STAB_BASE, + TCA_STAB_DATA, + __TCA_STAB_MAX +}; + +#define TCA_STAB_MAX (__TCA_STAB_MAX - 1) + /* FIFO section */ struct tc_fifo_qopt diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index b358c704d102..f4d386c191f5 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -482,6 +482,7 @@ enum TCA_RATE, TCA_FCNT, TCA_STATS2, + TCA_STAB, __TCA_MAX }; diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h index e4e30052e4e2..6affcfaa123e 100644 --- a/include/net/pkt_sched.h +++ b/include/net/pkt_sched.h @@ -83,6 +83,7 @@ extern struct Qdisc *qdisc_lookup_class(struct net_device *dev, u32 handle); extern struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r, struct nlattr *tab); extern void qdisc_put_rtab(struct qdisc_rate_table *tab); +extern void qdisc_put_stab(struct qdisc_size_table *tab); extern void __qdisc_run(struct Qdisc *q); diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 8229520e088a..db9ad655eb8a 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -29,6 +29,13 @@ enum qdisc_state_t __QDISC_STATE_SCHED, }; +struct qdisc_size_table { + struct list_head list; + struct tc_sizespec szopts; + int refcnt; + u16 data[]; +}; + struct Qdisc { int (*enqueue)(struct sk_buff *skb, struct Qdisc *dev); @@ -39,6 +46,7 @@ struct Qdisc #define TCQ_F_INGRESS 4 int padded; struct Qdisc_ops *ops; + struct qdisc_size_table *stab; u32 handle; u32 parent; atomic_t refcnt; @@ -165,6 +173,16 @@ struct tcf_proto struct tcf_proto_ops *ops; }; +struct qdisc_skb_cb { + unsigned int pkt_len; + char data[]; +}; + +static inline struct qdisc_skb_cb *qdisc_skb_cb(struct sk_buff *skb) +{ + return (struct qdisc_skb_cb *)skb->cb; +} + static inline spinlock_t *qdisc_lock(struct Qdisc *qdisc) { return &qdisc->q.lock; @@ -257,6 +275,8 @@ extern struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue, extern struct Qdisc *qdisc_create_dflt(struct net_device *dev, struct netdev_queue *dev_queue, struct Qdisc_ops *ops, u32 parentid); +extern void qdisc_calculate_pkt_len(struct sk_buff *skb, + struct qdisc_size_table *stab); extern void tcf_destroy(struct tcf_proto *tp); extern void tcf_destroy_chain(struct tcf_proto **fl); @@ -308,16 +328,19 @@ static inline bool qdisc_tx_is_noop(const struct net_device *dev) static inline unsigned int qdisc_pkt_len(struct sk_buff *skb) { - return skb->len; + return qdisc_skb_cb(skb)->pkt_len; } static inline int qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch) { + if (sch->stab) + qdisc_calculate_pkt_len(skb, sch->stab); return sch->enqueue(skb, sch); } static inline int qdisc_enqueue_root(struct sk_buff *skb, struct Qdisc *sch) { + qdisc_skb_cb(skb)->pkt_len = skb->len; return qdisc_enqueue(skb, sch); } diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index fb43731c9860..5219d5f9d754 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -286,6 +286,129 @@ void qdisc_put_rtab(struct qdisc_rate_table *tab) } EXPORT_SYMBOL(qdisc_put_rtab); +static LIST_HEAD(qdisc_stab_list); +static DEFINE_SPINLOCK(qdisc_stab_lock); + +static const struct nla_policy stab_policy[TCA_STAB_MAX + 1] = { + [TCA_STAB_BASE] = { .len = sizeof(struct tc_sizespec) }, + [TCA_STAB_DATA] = { .type = NLA_BINARY }, +}; + +static struct qdisc_size_table *qdisc_get_stab(struct nlattr *opt) +{ + struct nlattr *tb[TCA_STAB_MAX + 1]; + struct qdisc_size_table *stab; + struct tc_sizespec *s; + unsigned int tsize = 0; + u16 *tab = NULL; + int err; + + err = nla_parse_nested(tb, TCA_STAB_MAX, opt, stab_policy); + if (err < 0) + return ERR_PTR(err); + if (!tb[TCA_STAB_BASE]) + return ERR_PTR(-EINVAL); + + s = nla_data(tb[TCA_STAB_BASE]); + + if (s->tsize > 0) { + if (!tb[TCA_STAB_DATA]) + return ERR_PTR(-EINVAL); + tab = nla_data(tb[TCA_STAB_DATA]); + tsize = nla_len(tb[TCA_STAB_DATA]) / sizeof(u16); + } + + if (!s || tsize != s->tsize || (!tab && tsize > 0)) + return ERR_PTR(-EINVAL); + + spin_lock(&qdisc_stab_lock); + + list_for_each_entry(stab, &qdisc_stab_list, list) { + if (memcmp(&stab->szopts, s, sizeof(*s))) + continue; + if (tsize > 0 && memcmp(stab->data, tab, tsize * sizeof(u16))) + continue; + stab->refcnt++; + spin_unlock(&qdisc_stab_lock); + return stab; + } + + spin_unlock(&qdisc_stab_lock); + + stab = kmalloc(sizeof(*stab) + tsize * sizeof(u16), GFP_KERNEL); + if (!stab) + return ERR_PTR(-ENOMEM); + + stab->refcnt = 1; + stab->szopts = *s; + if (tsize > 0) + memcpy(stab->data, tab, tsize * sizeof(u16)); + + spin_lock(&qdisc_stab_lock); + list_add_tail(&stab->list, &qdisc_stab_list); + spin_unlock(&qdisc_stab_lock); + + return stab; +} + +void qdisc_put_stab(struct qdisc_size_table *tab) +{ + if (!tab) + return; + + spin_lock(&qdisc_stab_lock); + + if (--tab->refcnt == 0) { + list_del(&tab->list); + kfree(tab); + } + + spin_unlock(&qdisc_stab_lock); +} +EXPORT_SYMBOL(qdisc_put_stab); + +static int qdisc_dump_stab(struct sk_buff *skb, struct qdisc_size_table *stab) +{ + struct nlattr *nest; + + nest = nla_nest_start(skb, TCA_STAB); + NLA_PUT(skb, TCA_STAB_BASE, sizeof(stab->szopts), &stab->szopts); + nla_nest_end(skb, nest); + + return skb->len; + +nla_put_failure: + return -1; +} + +void qdisc_calculate_pkt_len(struct sk_buff *skb, struct qdisc_size_table *stab) +{ + int pkt_len, slot; + + pkt_len = skb->len + stab->szopts.overhead; + if (unlikely(!stab->szopts.tsize)) + goto out; + + slot = pkt_len + stab->szopts.cell_align; + if (unlikely(slot < 0)) + slot = 0; + + slot >>= stab->szopts.cell_log; + if (likely(slot < stab->szopts.tsize)) + pkt_len = stab->data[slot]; + else + pkt_len = stab->data[stab->szopts.tsize - 1] * + (slot / stab->szopts.tsize) + + stab->data[slot % stab->szopts.tsize]; + + pkt_len <<= stab->szopts.size_log; +out: + if (unlikely(pkt_len < 1)) + pkt_len = 1; + qdisc_skb_cb(skb)->pkt_len = pkt_len; +} +EXPORT_SYMBOL(qdisc_calculate_pkt_len); + static enum hrtimer_restart qdisc_watchdog(struct hrtimer *timer) { struct qdisc_watchdog *wd = container_of(timer, struct qdisc_watchdog, @@ -613,6 +736,7 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue, struct nlattr *kind = tca[TCA_KIND]; struct Qdisc *sch; struct Qdisc_ops *ops; + struct qdisc_size_table *stab; ops = qdisc_lookup_ops(kind); #ifdef CONFIG_KMOD @@ -670,6 +794,14 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue, sch->handle = handle; if (!ops->init || (err = ops->init(sch, tca[TCA_OPTIONS])) == 0) { + if (tca[TCA_STAB]) { + stab = qdisc_get_stab(tca[TCA_STAB]); + if (IS_ERR(stab)) { + err = PTR_ERR(stab); + goto err_out3; + } + sch->stab = stab; + } if (tca[TCA_RATE]) { err = gen_new_estimator(&sch->bstats, &sch->rate_est, qdisc_root_lock(sch), @@ -691,6 +823,7 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue, return sch; } err_out3: + qdisc_put_stab(sch->stab); dev_put(dev); kfree((char *) sch - sch->padded); err_out2: @@ -702,15 +835,26 @@ err_out: static int qdisc_change(struct Qdisc *sch, struct nlattr **tca) { - if (tca[TCA_OPTIONS]) { - int err; + struct qdisc_size_table *stab = NULL; + int err = 0; + if (tca[TCA_OPTIONS]) { if (sch->ops->change == NULL) return -EINVAL; err = sch->ops->change(sch, tca[TCA_OPTIONS]); if (err) return err; } + + if (tca[TCA_STAB]) { + stab = qdisc_get_stab(tca[TCA_STAB]); + if (IS_ERR(stab)) + return PTR_ERR(stab); + } + + qdisc_put_stab(sch->stab); + sch->stab = stab; + if (tca[TCA_RATE]) gen_replace_estimator(&sch->bstats, &sch->rate_est, qdisc_root_lock(sch), tca[TCA_RATE]); @@ -994,6 +1138,9 @@ static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid, goto nla_put_failure; q->qstats.qlen = q->q.qlen; + if (q->stab && qdisc_dump_stab(skb, q->stab) < 0) + goto nla_put_failure; + if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS, TCA_XSTATS, qdisc_root_lock(q), &d) < 0) goto nla_put_failure; diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 522a41a9f904..27a51f04db49 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -469,6 +469,7 @@ static void __qdisc_destroy(struct rcu_head *head) struct Qdisc *qdisc = container_of(head, struct Qdisc, q_rcu); const struct Qdisc_ops *ops = qdisc->ops; + qdisc_put_stab(qdisc->stab); gen_kill_estimator(&qdisc->bstats, &qdisc->rate_est); if (ops->reset) ops->reset(qdisc); diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index ae49be00022f..a59085700678 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -84,8 +84,9 @@ struct netem_skb_cb { static inline struct netem_skb_cb *netem_skb_cb(struct sk_buff *skb) { - BUILD_BUG_ON(sizeof(skb->cb) < sizeof(struct netem_skb_cb)); - return (struct netem_skb_cb *)skb->cb; + BUILD_BUG_ON(sizeof(skb->cb) < + sizeof(struct qdisc_skb_cb) + sizeof(struct netem_skb_cb)); + return (struct netem_skb_cb *)qdisc_skb_cb(skb)->data; } /* init_crandom - initialize correlated random number generator -- cgit v1.2.3 From 7cc8883074b040aa8c1ebd3a17463b0ea3a9ef16 Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Tue, 13 May 2008 16:29:20 +0300 Subject: KVM: Remove decache_vcpus_on_cpu() and related callbacks Obsoleted by the vmx-specific per-cpu list. Signed-off-by: Avi Kivity --- arch/ia64/kvm/kvm-ia64.c | 8 -------- arch/powerpc/kvm/powerpc.c | 4 ---- arch/s390/kvm/kvm-s390.c | 4 ---- arch/x86/kvm/svm.c | 5 ----- arch/x86/kvm/vmx.c | 6 ------ arch/x86/kvm/x86.c | 8 -------- include/asm-x86/kvm_host.h | 1 - include/linux/kvm_host.h | 3 --- virt/kvm/kvm_main.c | 1 - 9 files changed, 40 deletions(-) (limited to 'include/linux') diff --git a/arch/ia64/kvm/kvm-ia64.c b/arch/ia64/kvm/kvm-ia64.c index 68c978be9a51..7c504be57972 100644 --- a/arch/ia64/kvm/kvm-ia64.c +++ b/arch/ia64/kvm/kvm-ia64.c @@ -1035,14 +1035,6 @@ static void kvm_free_vmm_area(void) } } -/* - * Make sure that a cpu that is being hot-unplugged does not have any vcpus - * cached on it. Leave it as blank for IA64. - */ -void decache_vcpus_on_cpu(int cpu) -{ -} - static void vti_vcpu_load(struct kvm_vcpu *vcpu, int cpu) { } diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index 777e0f34e0ea..0513b359851b 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -240,10 +240,6 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) { } -void decache_vcpus_on_cpu(int cpu) -{ -} - int kvm_arch_vcpu_ioctl_debug_guest(struct kvm_vcpu *vcpu, struct kvm_debug_guest *dbg) { diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 6558b09ff579..4585c8ac2b0c 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -79,10 +79,6 @@ void kvm_arch_hardware_disable(void *garbage) { } -void decache_vcpus_on_cpu(int cpu) -{ -} - int kvm_arch_hardware_setup(void) { return 0; diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index 9390a31c06f4..238e8f3afaf4 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c @@ -709,10 +709,6 @@ static void svm_vcpu_put(struct kvm_vcpu *vcpu) rdtscll(vcpu->arch.host_tsc); } -static void svm_vcpu_decache(struct kvm_vcpu *vcpu) -{ -} - static void svm_cache_regs(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); @@ -1933,7 +1929,6 @@ static struct kvm_x86_ops svm_x86_ops = { .prepare_guest_switch = svm_prepare_guest_switch, .vcpu_load = svm_vcpu_load, .vcpu_put = svm_vcpu_put, - .vcpu_decache = svm_vcpu_decache, .set_guest_debug = svm_guest_debug, .get_msr = svm_get_msr, diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 4d179d106376..b99bb37e5dec 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -692,11 +692,6 @@ static void vmx_fpu_deactivate(struct kvm_vcpu *vcpu) update_exception_bitmap(vcpu); } -static void vmx_vcpu_decache(struct kvm_vcpu *vcpu) -{ - vcpu_clear(to_vmx(vcpu)); -} - static unsigned long vmx_get_rflags(struct kvm_vcpu *vcpu) { return vmcs_readl(GUEST_RFLAGS); @@ -3114,7 +3109,6 @@ static struct kvm_x86_ops vmx_x86_ops = { .prepare_guest_switch = vmx_save_host_state, .vcpu_load = vmx_vcpu_load, .vcpu_put = vmx_vcpu_put, - .vcpu_decache = vmx_vcpu_decache, .set_guest_debug = set_guest_debug, .guest_debug_pre = kvm_guest_debug_pre, diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 8c14ddcaba70..fd03b4465bcc 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -817,14 +817,6 @@ out: return r; } -/* - * Make sure that a cpu that is being hot-unplugged does not have any vcpus - * cached on it. - */ -void decache_vcpus_on_cpu(int cpu) -{ -} - int kvm_dev_ioctl_check_extension(long ext) { int r; diff --git a/include/asm-x86/kvm_host.h b/include/asm-x86/kvm_host.h index 0df9d5fa281a..4bcdc7de07b5 100644 --- a/include/asm-x86/kvm_host.h +++ b/include/asm-x86/kvm_host.h @@ -380,7 +380,6 @@ struct kvm_x86_ops { void (*prepare_guest_switch)(struct kvm_vcpu *vcpu); void (*vcpu_load)(struct kvm_vcpu *vcpu, int cpu); void (*vcpu_put)(struct kvm_vcpu *vcpu); - void (*vcpu_decache)(struct kvm_vcpu *vcpu); int (*set_guest_debug)(struct kvm_vcpu *vcpu, struct kvm_debug_guest *dbg); diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index de9d1df4bba2..865dcbcb891f 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -135,9 +135,6 @@ void kvm_vcpu_uninit(struct kvm_vcpu *vcpu); void vcpu_load(struct kvm_vcpu *vcpu); void vcpu_put(struct kvm_vcpu *vcpu); -void decache_vcpus_on_cpu(int cpu); - - int kvm_init(void *opaque, unsigned int vcpu_size, struct module *module); void kvm_exit(void); diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index e4bf88a9ee4e..83a0e5ce6037 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -1273,7 +1273,6 @@ static void hardware_disable(void *junk) if (!cpu_isset(cpu, cpus_hardware_enabled)) return; cpu_clear(cpu, cpus_hardware_enabled); - decache_vcpus_on_cpu(cpu); kvm_arch_hardware_disable(NULL); } -- cgit v1.2.3 From 92760499d01ef91518119908eb9b8798b6c9bd3f Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Fri, 30 May 2008 16:05:53 +0200 Subject: KVM: kvm_io_device: extend in_range() to manage len and write attribute Modify member in_range() of structure kvm_io_device to pass length and the type of the I/O (write or read). This modification allows to use kvm_io_device with coalesced MMIO. Signed-off-by: Laurent Vivier Signed-off-by: Avi Kivity --- arch/ia64/kvm/kvm-ia64.c | 6 +++--- arch/x86/kvm/i8254.c | 6 ++++-- arch/x86/kvm/i8259.c | 3 ++- arch/x86/kvm/lapic.c | 3 ++- arch/x86/kvm/x86.c | 28 +++++++++++++++++----------- include/linux/kvm_host.h | 3 ++- virt/kvm/ioapic.c | 3 ++- virt/kvm/iodev.h | 8 +++++--- virt/kvm/kvm_main.c | 5 +++-- 9 files changed, 40 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/arch/ia64/kvm/kvm-ia64.c b/arch/ia64/kvm/kvm-ia64.c index 7c504be57972..bb58df7cc418 100644 --- a/arch/ia64/kvm/kvm-ia64.c +++ b/arch/ia64/kvm/kvm-ia64.c @@ -195,11 +195,11 @@ int kvm_dev_ioctl_check_extension(long ext) } static struct kvm_io_device *vcpu_find_mmio_dev(struct kvm_vcpu *vcpu, - gpa_t addr) + gpa_t addr, int len, int is_write) { struct kvm_io_device *dev; - dev = kvm_io_bus_find_dev(&vcpu->kvm->mmio_bus, addr); + dev = kvm_io_bus_find_dev(&vcpu->kvm->mmio_bus, addr, len, is_write); return dev; } @@ -231,7 +231,7 @@ static int handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) kvm_run->exit_reason = KVM_EXIT_MMIO; return 0; mmio: - mmio_dev = vcpu_find_mmio_dev(vcpu, p->addr); + mmio_dev = vcpu_find_mmio_dev(vcpu, p->addr, p->size, !p->dir); if (mmio_dev) { if (!p->dir) kvm_iodevice_write(mmio_dev, p->addr, p->size, diff --git a/arch/x86/kvm/i8254.c b/arch/x86/kvm/i8254.c index 60074dc66bd7..9e3391e9a1b7 100644 --- a/arch/x86/kvm/i8254.c +++ b/arch/x86/kvm/i8254.c @@ -460,7 +460,8 @@ static void pit_ioport_read(struct kvm_io_device *this, mutex_unlock(&pit_state->lock); } -static int pit_in_range(struct kvm_io_device *this, gpa_t addr) +static int pit_in_range(struct kvm_io_device *this, gpa_t addr, + int len, int is_write) { return ((addr >= KVM_PIT_BASE_ADDRESS) && (addr < KVM_PIT_BASE_ADDRESS + KVM_PIT_MEM_LENGTH)); @@ -501,7 +502,8 @@ static void speaker_ioport_read(struct kvm_io_device *this, mutex_unlock(&pit_state->lock); } -static int speaker_in_range(struct kvm_io_device *this, gpa_t addr) +static int speaker_in_range(struct kvm_io_device *this, gpa_t addr, + int len, int is_write) { return (addr == KVM_SPEAKER_BASE_ADDRESS); } diff --git a/arch/x86/kvm/i8259.c b/arch/x86/kvm/i8259.c index ab29cf2def47..5857f59ad4aa 100644 --- a/arch/x86/kvm/i8259.c +++ b/arch/x86/kvm/i8259.c @@ -346,7 +346,8 @@ static u32 elcr_ioport_read(void *opaque, u32 addr1) return s->elcr; } -static int picdev_in_range(struct kvm_io_device *this, gpa_t addr) +static int picdev_in_range(struct kvm_io_device *this, gpa_t addr, + int len, int is_write) { switch (addr) { case 0x20: diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index e48d19394031..180ba7316da5 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -785,7 +785,8 @@ static void apic_mmio_write(struct kvm_io_device *this, } -static int apic_mmio_range(struct kvm_io_device *this, gpa_t addr) +static int apic_mmio_range(struct kvm_io_device *this, gpa_t addr, + int len, int size) { struct kvm_lapic *apic = (struct kvm_lapic *)this->private; int ret = 0; diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 4c94fad7f01e..ab3f5552d694 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -1797,13 +1797,14 @@ static void kvm_init_msr_list(void) * Only apic need an MMIO device hook, so shortcut now.. */ static struct kvm_io_device *vcpu_find_pervcpu_dev(struct kvm_vcpu *vcpu, - gpa_t addr) + gpa_t addr, int len, + int is_write) { struct kvm_io_device *dev; if (vcpu->arch.apic) { dev = &vcpu->arch.apic->dev; - if (dev->in_range(dev, addr)) + if (dev->in_range(dev, addr, len, is_write)) return dev; } return NULL; @@ -1811,13 +1812,15 @@ static struct kvm_io_device *vcpu_find_pervcpu_dev(struct kvm_vcpu *vcpu, static struct kvm_io_device *vcpu_find_mmio_dev(struct kvm_vcpu *vcpu, - gpa_t addr) + gpa_t addr, int len, + int is_write) { struct kvm_io_device *dev; - dev = vcpu_find_pervcpu_dev(vcpu, addr); + dev = vcpu_find_pervcpu_dev(vcpu, addr, len, is_write); if (dev == NULL) - dev = kvm_io_bus_find_dev(&vcpu->kvm->mmio_bus, addr); + dev = kvm_io_bus_find_dev(&vcpu->kvm->mmio_bus, addr, len, + is_write); return dev; } @@ -1885,7 +1888,7 @@ mmio: * Is this MMIO handled locally? */ mutex_lock(&vcpu->kvm->lock); - mmio_dev = vcpu_find_mmio_dev(vcpu, gpa); + mmio_dev = vcpu_find_mmio_dev(vcpu, gpa, bytes, 0); if (mmio_dev) { kvm_iodevice_read(mmio_dev, gpa, bytes, val); mutex_unlock(&vcpu->kvm->lock); @@ -1940,7 +1943,7 @@ mmio: * Is this MMIO handled locally? */ mutex_lock(&vcpu->kvm->lock); - mmio_dev = vcpu_find_mmio_dev(vcpu, gpa); + mmio_dev = vcpu_find_mmio_dev(vcpu, gpa, bytes, 1); if (mmio_dev) { kvm_iodevice_write(mmio_dev, gpa, bytes, val); mutex_unlock(&vcpu->kvm->lock); @@ -2317,9 +2320,10 @@ static void pio_string_write(struct kvm_io_device *pio_dev, } static struct kvm_io_device *vcpu_find_pio_dev(struct kvm_vcpu *vcpu, - gpa_t addr) + gpa_t addr, int len, + int is_write) { - return kvm_io_bus_find_dev(&vcpu->kvm->pio_bus, addr); + return kvm_io_bus_find_dev(&vcpu->kvm->pio_bus, addr, len, is_write); } int kvm_emulate_pio(struct kvm_vcpu *vcpu, struct kvm_run *run, int in, @@ -2351,7 +2355,7 @@ int kvm_emulate_pio(struct kvm_vcpu *vcpu, struct kvm_run *run, int in, kvm_x86_ops->skip_emulated_instruction(vcpu); - pio_dev = vcpu_find_pio_dev(vcpu, port); + pio_dev = vcpu_find_pio_dev(vcpu, port, size, !in); if (pio_dev) { kernel_pio(pio_dev, vcpu, vcpu->arch.pio_data); complete_pio(vcpu); @@ -2433,7 +2437,9 @@ int kvm_emulate_pio_string(struct kvm_vcpu *vcpu, struct kvm_run *run, int in, } } - pio_dev = vcpu_find_pio_dev(vcpu, port); + pio_dev = vcpu_find_pio_dev(vcpu, port, + vcpu->arch.pio.cur_count, + !vcpu->arch.pio.in); if (!vcpu->arch.pio.in) { /* string PIO write */ ret = pio_copy_data(vcpu); diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 865dcbcb891f..499ff0604234 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -52,7 +52,8 @@ struct kvm_io_bus { void kvm_io_bus_init(struct kvm_io_bus *bus); void kvm_io_bus_destroy(struct kvm_io_bus *bus); -struct kvm_io_device *kvm_io_bus_find_dev(struct kvm_io_bus *bus, gpa_t addr); +struct kvm_io_device *kvm_io_bus_find_dev(struct kvm_io_bus *bus, + gpa_t addr, int len, int is_write); void kvm_io_bus_register_dev(struct kvm_io_bus *bus, struct kvm_io_device *dev); diff --git a/virt/kvm/ioapic.c b/virt/kvm/ioapic.c index d0c668c6959e..c0d22870ee9c 100644 --- a/virt/kvm/ioapic.c +++ b/virt/kvm/ioapic.c @@ -307,7 +307,8 @@ void kvm_ioapic_update_eoi(struct kvm *kvm, int vector) __kvm_ioapic_update_eoi(ioapic, i); } -static int ioapic_in_range(struct kvm_io_device *this, gpa_t addr) +static int ioapic_in_range(struct kvm_io_device *this, gpa_t addr, + int len, int is_write) { struct kvm_ioapic *ioapic = (struct kvm_ioapic *)this->private; diff --git a/virt/kvm/iodev.h b/virt/kvm/iodev.h index c14e642027b2..55e8846ac3a6 100644 --- a/virt/kvm/iodev.h +++ b/virt/kvm/iodev.h @@ -27,7 +27,8 @@ struct kvm_io_device { gpa_t addr, int len, const void *val); - int (*in_range)(struct kvm_io_device *this, gpa_t addr); + int (*in_range)(struct kvm_io_device *this, gpa_t addr, int len, + int is_write); void (*destructor)(struct kvm_io_device *this); void *private; @@ -49,9 +50,10 @@ static inline void kvm_iodevice_write(struct kvm_io_device *dev, dev->write(dev, addr, len, val); } -static inline int kvm_iodevice_inrange(struct kvm_io_device *dev, gpa_t addr) +static inline int kvm_iodevice_inrange(struct kvm_io_device *dev, + gpa_t addr, int len, int is_write) { - return dev->in_range(dev, addr); + return dev->in_range(dev, addr, len, is_write); } static inline void kvm_iodevice_destructor(struct kvm_io_device *dev) diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 83a0e5ce6037..9330fad2b918 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -1350,14 +1350,15 @@ void kvm_io_bus_destroy(struct kvm_io_bus *bus) } } -struct kvm_io_device *kvm_io_bus_find_dev(struct kvm_io_bus *bus, gpa_t addr) +struct kvm_io_device *kvm_io_bus_find_dev(struct kvm_io_bus *bus, + gpa_t addr, int len, int is_write) { int i; for (i = 0; i < bus->dev_count; i++) { struct kvm_io_device *pos = bus->devs[i]; - if (pos->in_range(pos, addr)) + if (pos->in_range(pos, addr, len, is_write)) return pos; } -- cgit v1.2.3 From 5f94c1741bdc7a336553122036e8a779e616ccbf Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Fri, 30 May 2008 16:05:54 +0200 Subject: KVM: Add coalesced MMIO support (common part) This patch adds all needed structures to coalesce MMIOs. Until an architecture uses it, it is not compiled. Coalesced MMIO introduces two ioctl() to define where are the MMIO zones that can be coalesced: - KVM_REGISTER_COALESCED_MMIO registers a coalesced MMIO zone. It requests one parameter (struct kvm_coalesced_mmio_zone) which defines a memory area where MMIOs can be coalesced until the next switch to user space. The maximum number of MMIO zones is KVM_COALESCED_MMIO_ZONE_MAX. - KVM_UNREGISTER_COALESCED_MMIO cancels all registered zones inside the given bounds (bounds are also given by struct kvm_coalesced_mmio_zone). The userspace client can check kernel coalesced MMIO availability by asking ioctl(KVM_CHECK_EXTENSION) for the KVM_CAP_COALESCED_MMIO capability. The ioctl() call to KVM_CAP_COALESCED_MMIO will return 0 if not supported, or the page offset where will be stored the ring buffer. The page offset depends on the architecture. After an ioctl(KVM_RUN), the first page of the KVM memory mapped points to a kvm_run structure. The offset given by KVM_CAP_COALESCED_MMIO is an offset to the coalesced MMIO ring expressed in PAGE_SIZE relatively to the address of the start of th kvm_run structure. The MMIO ring buffer is defined by the structure kvm_coalesced_mmio_ring. [akio: fix oops during guest shutdown] Signed-off-by: Laurent Vivier Signed-off-by: Akio Takebe Signed-off-by: Avi Kivity --- include/linux/kvm.h | 29 +++++++++ include/linux/kvm_host.h | 4 ++ virt/kvm/coalesced_mmio.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++ virt/kvm/coalesced_mmio.h | 23 +++++++ virt/kvm/kvm_main.c | 57 +++++++++++++++++ 5 files changed, 269 insertions(+) create mode 100644 virt/kvm/coalesced_mmio.c create mode 100644 virt/kvm/coalesced_mmio.h (limited to 'include/linux') diff --git a/include/linux/kvm.h b/include/linux/kvm.h index a281afeddfbb..1c908ac29c6c 100644 --- a/include/linux/kvm.h +++ b/include/linux/kvm.h @@ -173,6 +173,30 @@ struct kvm_run { }; }; +/* for KVM_REGISTER_COALESCED_MMIO / KVM_UNREGISTER_COALESCED_MMIO */ + +struct kvm_coalesced_mmio_zone { + __u64 addr; + __u32 size; + __u32 pad; +}; + +struct kvm_coalesced_mmio { + __u64 phys_addr; + __u32 len; + __u32 pad; + __u8 data[8]; +}; + +struct kvm_coalesced_mmio_ring { + __u32 first, last; + struct kvm_coalesced_mmio coalesced_mmio[0]; +}; + +#define KVM_COALESCED_MMIO_MAX \ + ((PAGE_SIZE - sizeof(struct kvm_coalesced_mmio_ring)) / \ + sizeof(struct kvm_coalesced_mmio)) + /* for KVM_TRANSLATE */ struct kvm_translation { /* in */ @@ -346,6 +370,7 @@ struct kvm_trace_rec { #define KVM_CAP_NOP_IO_DELAY 12 #define KVM_CAP_PV_MMU 13 #define KVM_CAP_MP_STATE 14 +#define KVM_CAP_COALESCED_MMIO 15 /* * ioctls for VM fds @@ -371,6 +396,10 @@ struct kvm_trace_rec { #define KVM_CREATE_PIT _IO(KVMIO, 0x64) #define KVM_GET_PIT _IOWR(KVMIO, 0x65, struct kvm_pit_state) #define KVM_SET_PIT _IOR(KVMIO, 0x66, struct kvm_pit_state) +#define KVM_REGISTER_COALESCED_MMIO \ + _IOW(KVMIO, 0x67, struct kvm_coalesced_mmio_zone) +#define KVM_UNREGISTER_COALESCED_MMIO \ + _IOW(KVMIO, 0x68, struct kvm_coalesced_mmio_zone) /* * ioctls for vcpu fds diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 499ff0604234..d220b4926c4a 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -117,6 +117,10 @@ struct kvm { struct kvm_vm_stat stat; struct kvm_arch arch; atomic_t users_count; +#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET + struct kvm_coalesced_mmio_dev *coalesced_mmio_dev; + struct kvm_coalesced_mmio_ring *coalesced_mmio_ring; +#endif }; /* The guest did something we don't support. */ diff --git a/virt/kvm/coalesced_mmio.c b/virt/kvm/coalesced_mmio.c new file mode 100644 index 000000000000..5ae620d32fac --- /dev/null +++ b/virt/kvm/coalesced_mmio.c @@ -0,0 +1,156 @@ +/* + * KVM coalesced MMIO + * + * Copyright (c) 2008 Bull S.A.S. + * + * Author: Laurent Vivier + * + */ + +#include "iodev.h" + +#include +#include + +#include "coalesced_mmio.h" + +static int coalesced_mmio_in_range(struct kvm_io_device *this, + gpa_t addr, int len, int is_write) +{ + struct kvm_coalesced_mmio_dev *dev = + (struct kvm_coalesced_mmio_dev*)this->private; + struct kvm_coalesced_mmio_zone *zone; + int next; + int i; + + if (!is_write) + return 0; + + /* kvm->lock is taken by the caller and must be not released before + * dev.read/write + */ + + /* Are we able to batch it ? */ + + /* last is the first free entry + * check if we don't meet the first used entry + * there is always one unused entry in the buffer + */ + + next = (dev->kvm->coalesced_mmio_ring->last + 1) % + KVM_COALESCED_MMIO_MAX; + if (next == dev->kvm->coalesced_mmio_ring->first) { + /* full */ + return 0; + } + + /* is it in a batchable area ? */ + + for (i = 0; i < dev->nb_zones; i++) { + zone = &dev->zone[i]; + + /* (addr,len) is fully included in + * (zone->addr, zone->size) + */ + + if (zone->addr <= addr && + addr + len <= zone->addr + zone->size) + return 1; + } + return 0; +} + +static void coalesced_mmio_write(struct kvm_io_device *this, + gpa_t addr, int len, const void *val) +{ + struct kvm_coalesced_mmio_dev *dev = + (struct kvm_coalesced_mmio_dev*)this->private; + struct kvm_coalesced_mmio_ring *ring = dev->kvm->coalesced_mmio_ring; + + /* kvm->lock must be taken by caller before call to in_range()*/ + + /* copy data in first free entry of the ring */ + + ring->coalesced_mmio[ring->last].phys_addr = addr; + ring->coalesced_mmio[ring->last].len = len; + memcpy(ring->coalesced_mmio[ring->last].data, val, len); + smp_wmb(); + ring->last = (ring->last + 1) % KVM_COALESCED_MMIO_MAX; +} + +static void coalesced_mmio_destructor(struct kvm_io_device *this) +{ + kfree(this); +} + +int kvm_coalesced_mmio_init(struct kvm *kvm) +{ + struct kvm_coalesced_mmio_dev *dev; + + dev = kzalloc(sizeof(struct kvm_coalesced_mmio_dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + dev->dev.write = coalesced_mmio_write; + dev->dev.in_range = coalesced_mmio_in_range; + dev->dev.destructor = coalesced_mmio_destructor; + dev->dev.private = dev; + dev->kvm = kvm; + kvm->coalesced_mmio_dev = dev; + kvm_io_bus_register_dev(&kvm->mmio_bus, &dev->dev); + + return 0; +} + +int kvm_vm_ioctl_register_coalesced_mmio(struct kvm *kvm, + struct kvm_coalesced_mmio_zone *zone) +{ + struct kvm_coalesced_mmio_dev *dev = kvm->coalesced_mmio_dev; + + if (dev == NULL) + return -EINVAL; + + mutex_lock(&kvm->lock); + if (dev->nb_zones >= KVM_COALESCED_MMIO_ZONE_MAX) { + mutex_unlock(&kvm->lock); + return -ENOBUFS; + } + + dev->zone[dev->nb_zones] = *zone; + dev->nb_zones++; + + mutex_unlock(&kvm->lock); + return 0; +} + +int kvm_vm_ioctl_unregister_coalesced_mmio(struct kvm *kvm, + struct kvm_coalesced_mmio_zone *zone) +{ + int i; + struct kvm_coalesced_mmio_dev *dev = kvm->coalesced_mmio_dev; + struct kvm_coalesced_mmio_zone *z; + + if (dev == NULL) + return -EINVAL; + + mutex_lock(&kvm->lock); + + i = dev->nb_zones; + while(i) { + z = &dev->zone[i - 1]; + + /* unregister all zones + * included in (zone->addr, zone->size) + */ + + if (zone->addr <= z->addr && + z->addr + z->size <= zone->addr + zone->size) { + dev->nb_zones--; + *z = dev->zone[dev->nb_zones]; + } + i--; + } + + mutex_unlock(&kvm->lock); + + return 0; +} diff --git a/virt/kvm/coalesced_mmio.h b/virt/kvm/coalesced_mmio.h new file mode 100644 index 000000000000..5ac0ec628461 --- /dev/null +++ b/virt/kvm/coalesced_mmio.h @@ -0,0 +1,23 @@ +/* + * KVM coalesced MMIO + * + * Copyright (c) 2008 Bull S.A.S. + * + * Author: Laurent Vivier + * + */ + +#define KVM_COALESCED_MMIO_ZONE_MAX 100 + +struct kvm_coalesced_mmio_dev { + struct kvm_io_device dev; + struct kvm *kvm; + int nb_zones; + struct kvm_coalesced_mmio_zone zone[KVM_COALESCED_MMIO_ZONE_MAX]; +}; + +int kvm_coalesced_mmio_init(struct kvm *kvm); +int kvm_vm_ioctl_register_coalesced_mmio(struct kvm *kvm, + struct kvm_coalesced_mmio_zone *zone); +int kvm_vm_ioctl_unregister_coalesced_mmio(struct kvm *kvm, + struct kvm_coalesced_mmio_zone *zone); diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 9330fad2b918..7d10dfa0d388 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -47,6 +47,10 @@ #include #include +#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET +#include "coalesced_mmio.h" +#endif + MODULE_AUTHOR("Qumranet"); MODULE_LICENSE("GPL"); @@ -185,10 +189,23 @@ EXPORT_SYMBOL_GPL(kvm_vcpu_uninit); static struct kvm *kvm_create_vm(void) { struct kvm *kvm = kvm_arch_create_vm(); +#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET + struct page *page; +#endif if (IS_ERR(kvm)) goto out; +#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET + page = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (!page) { + kfree(kvm); + return ERR_PTR(-ENOMEM); + } + kvm->coalesced_mmio_ring = + (struct kvm_coalesced_mmio_ring *)page_address(page); +#endif + kvm->mm = current->mm; atomic_inc(&kvm->mm->mm_count); spin_lock_init(&kvm->mmu_lock); @@ -200,6 +217,9 @@ static struct kvm *kvm_create_vm(void) spin_lock(&kvm_lock); list_add(&kvm->vm_list, &vm_list); spin_unlock(&kvm_lock); +#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET + kvm_coalesced_mmio_init(kvm); +#endif out: return kvm; } @@ -242,6 +262,10 @@ static void kvm_destroy_vm(struct kvm *kvm) spin_unlock(&kvm_lock); kvm_io_bus_destroy(&kvm->pio_bus); kvm_io_bus_destroy(&kvm->mmio_bus); +#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET + if (kvm->coalesced_mmio_ring != NULL) + free_page((unsigned long)kvm->coalesced_mmio_ring); +#endif kvm_arch_destroy_vm(kvm); mmdrop(mm); } @@ -825,6 +849,10 @@ static int kvm_vcpu_fault(struct vm_area_struct *vma, struct vm_fault *vmf) #ifdef CONFIG_X86 else if (vmf->pgoff == KVM_PIO_PAGE_OFFSET) page = virt_to_page(vcpu->arch.pio_data); +#endif +#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET + else if (vmf->pgoff == KVM_COALESCED_MMIO_PAGE_OFFSET) + page = virt_to_page(vcpu->kvm->coalesced_mmio_ring); #endif else return VM_FAULT_SIGBUS; @@ -1148,6 +1176,32 @@ static long kvm_vm_ioctl(struct file *filp, goto out; break; } +#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET + case KVM_REGISTER_COALESCED_MMIO: { + struct kvm_coalesced_mmio_zone zone; + r = -EFAULT; + if (copy_from_user(&zone, argp, sizeof zone)) + goto out; + r = -ENXIO; + r = kvm_vm_ioctl_register_coalesced_mmio(kvm, &zone); + if (r) + goto out; + r = 0; + break; + } + case KVM_UNREGISTER_COALESCED_MMIO: { + struct kvm_coalesced_mmio_zone zone; + r = -EFAULT; + if (copy_from_user(&zone, argp, sizeof zone)) + goto out; + r = -ENXIO; + r = kvm_vm_ioctl_unregister_coalesced_mmio(kvm, &zone); + if (r) + goto out; + r = 0; + break; + } +#endif default: r = kvm_arch_vm_ioctl(filp, ioctl, arg); } @@ -1231,6 +1285,9 @@ static long kvm_dev_ioctl(struct file *filp, r = PAGE_SIZE; /* struct kvm_run */ #ifdef CONFIG_X86 r += PAGE_SIZE; /* pio data page */ +#endif +#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET + r += PAGE_SIZE; /* coalesced mmio ring page */ #endif break; case KVM_TRACE_ENABLE: -- cgit v1.2.3 From 9ef621d3be56e1188300476a8102ff54f7b6793f Mon Sep 17 00:00:00 2001 From: "Tan, Li" Date: Fri, 23 May 2008 14:54:09 +0800 Subject: KVM: Support mixed endian machines Currently kvmtrace is not portable. This will prevent from copying a trace file from big-endian target to little-endian workstation for analysis. In the patch, kernel outputs metadata containing a magic number to trace log, and changes 64-bit words to be u64 instead of a pair of u32s. Signed-off-by: Tan Li Acked-by: Jerone Young Acked-by: Hollis Blanchard Signed-off-by: Avi Kivity --- include/linux/kvm.h | 4 ++-- virt/kvm/kvm_trace.c | 18 ++++++++++++------ 2 files changed, 14 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kvm.h b/include/linux/kvm.h index 1c908ac29c6c..0ea064cbfbc8 100644 --- a/include/linux/kvm.h +++ b/include/linux/kvm.h @@ -318,14 +318,14 @@ struct kvm_trace_rec { __u32 vcpu_id; union { struct { - __u32 cycle_lo, cycle_hi; + __u64 cycle_u64; __u32 extra_u32[KVM_TRC_EXTRA_MAX]; } cycle; struct { __u32 extra_u32[KVM_TRC_EXTRA_MAX]; } nocycle; } u; -}; +} __attribute__((packed)); #define KVMIO 0xAE diff --git a/virt/kvm/kvm_trace.c b/virt/kvm/kvm_trace.c index 0e495470788d..58141f31ea8f 100644 --- a/virt/kvm/kvm_trace.c +++ b/virt/kvm/kvm_trace.c @@ -72,11 +72,7 @@ static void kvm_add_trace(void *probe_private, void *call_data, rec.cycle_in = p->cycle_in; if (rec.cycle_in) { - u64 cycle = 0; - - cycle = get_cycles(); - rec.u.cycle.cycle_lo = (u32)cycle; - rec.u.cycle.cycle_hi = (u32)(cycle >> 32); + rec.u.cycle.cycle_u64 = get_cycles(); for (i = 0; i < rec.extra_u32; i++) rec.u.cycle.extra_u32[i] = va_arg(*args, u32); @@ -114,8 +110,18 @@ static int kvm_subbuf_start_callback(struct rchan_buf *buf, void *subbuf, { struct kvm_trace *kt; - if (!relay_buf_full(buf)) + if (!relay_buf_full(buf)) { + if (!prev_subbuf) { + /* + * executed only once when the channel is opened + * save metadata as first record + */ + subbuf_start_reserve(buf, sizeof(u32)); + *(u32 *)subbuf = 0x12345678; + } + return 1; + } kt = buf->chan->private_data; atomic_inc(&kt->lost_records); -- cgit v1.2.3 From 34d4cb8fca1f2a31be152b74797e6cd160ec9de6 Mon Sep 17 00:00:00 2001 From: Marcelo Tosatti Date: Thu, 10 Jul 2008 20:49:31 -0300 Subject: KVM: MMU: nuke shadowed pgtable pages and ptes on memslot destruction Flush the shadow mmu before removing regions to avoid stale entries. Signed-off-by: Marcelo Tosatti Signed-off-by: Avi Kivity --- arch/ia64/kvm/kvm-ia64.c | 3 +++ arch/powerpc/kvm/powerpc.c | 4 ++++ arch/s390/kvm/kvm-s390.c | 4 ++++ arch/x86/kvm/x86.c | 5 +++++ include/linux/kvm_host.h | 1 + virt/kvm/kvm_main.c | 3 +++ 6 files changed, 20 insertions(+) (limited to 'include/linux') diff --git a/arch/ia64/kvm/kvm-ia64.c b/arch/ia64/kvm/kvm-ia64.c index 9408b30576d6..2672f4d278ac 100644 --- a/arch/ia64/kvm/kvm-ia64.c +++ b/arch/ia64/kvm/kvm-ia64.c @@ -1455,6 +1455,9 @@ int kvm_arch_set_memory_region(struct kvm *kvm, return 0; } +void kvm_arch_flush_shadow(struct kvm *kvm) +{ +} long kvm_arch_dev_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index b850d2497027..53826a5f6c06 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -170,6 +170,10 @@ int kvm_arch_set_memory_region(struct kvm *kvm, return 0; } +void kvm_arch_flush_shadow(struct kvm *kvm) +{ +} + struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id) { struct kvm_vcpu *vcpu; diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 399acf3f64dd..1782cbcd2829 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -675,6 +675,10 @@ int kvm_arch_set_memory_region(struct kvm *kvm, return 0; } +void kvm_arch_flush_shadow(struct kvm *kvm) +{ +} + gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn) { return gfn; diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index b131f3c0cf64..9f1cdb011cff 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -4032,6 +4032,11 @@ int kvm_arch_set_memory_region(struct kvm *kvm, return 0; } +void kvm_arch_flush_shadow(struct kvm *kvm) +{ + kvm_mmu_zap_all(kvm); +} + int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu) { return vcpu->arch.mp_state == KVM_MP_STATE_RUNNABLE diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index d220b4926c4a..07d68a8ae8e9 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -168,6 +168,7 @@ int kvm_arch_set_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem, struct kvm_memory_slot old, int user_alloc); +void kvm_arch_flush_shadow(struct kvm *kvm); gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn); struct page *gfn_to_page(struct kvm *kvm, gfn_t gfn); unsigned long gfn_to_hva(struct kvm *kvm, gfn_t gfn); diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 9ccaf8f5402e..30b36368fcdf 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -405,6 +405,9 @@ int __kvm_set_memory_region(struct kvm *kvm, if (mem->slot >= kvm->nmemslots) kvm->nmemslots = mem->slot + 1; + if (!npages) + kvm_arch_flush_shadow(kvm); + *memslot = new; r = kvm_arch_set_memory_region(kvm, mem, old, user_alloc); -- cgit v1.2.3 From 1d0ba5f3784612fe6e91a12e0dec37c797d4f07c Mon Sep 17 00:00:00 2001 From: Tobias Lorenz Date: Mon, 26 May 2008 18:40:46 -0300 Subject: V4L/DVB (7942): Hardware frequency seek ioctl interface Signed-off-by: Tobias Lorenz Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/compat_ioctl32.c | 1 + drivers/media/video/videodev.c | 12 ++++++++++++ include/linux/videodev2.h | 10 ++++++++++ include/media/v4l2-dev.h | 2 ++ 4 files changed, 25 insertions(+) (limited to 'include/linux') diff --git a/drivers/media/video/compat_ioctl32.c b/drivers/media/video/compat_ioctl32.c index cefd1381e8de..54de0cd482e9 100644 --- a/drivers/media/video/compat_ioctl32.c +++ b/drivers/media/video/compat_ioctl32.c @@ -884,6 +884,7 @@ long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) case VIDIOC_G_INPUT32: case VIDIOC_S_INPUT32: case VIDIOC_TRY_FMT32: + case VIDIOC_S_HW_FREQ_SEEK: ret = do_video_ioctl(file, cmd, arg); break; diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c index 7649860a388d..52c56678ee69 100644 --- a/drivers/media/video/videodev.c +++ b/drivers/media/video/videodev.c @@ -278,6 +278,7 @@ static const char *v4l2_ioctls[] = { [_IOC_NR(VIDIOC_DBG_G_REGISTER)] = "VIDIOC_DBG_G_REGISTER", [_IOC_NR(VIDIOC_G_CHIP_IDENT)] = "VIDIOC_G_CHIP_IDENT", + [_IOC_NR(VIDIOC_S_HW_FREQ_SEEK)] = "VIDIOC_S_HW_FREQ_SEEK", #endif }; #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) @@ -1763,6 +1764,17 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, ret = vfd->vidioc_default(file, fh, cmd, arg); break; } + case VIDIOC_S_HW_FREQ_SEEK: + { + struct v4l2_hw_freq_seek *p = arg; + if (!vfd->vidioc_s_hw_freq_seek) + break; + dbgarg(cmd, + "tuner=%d, type=%d, seek_upward=%d, wrap_around=%d\n", + p->tuner, p->type, p->seek_upward, p->wrap_around); + ret = vfd->vidioc_s_hw_freq_seek(file, fh, p); + break; + } } /* switch */ if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) { diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 4a535ea1e123..9385c823a978 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -246,6 +246,7 @@ struct v4l2_capability #define V4L2_CAP_SLICED_VBI_OUTPUT 0x00000080 /* Is a sliced VBI output device */ #define V4L2_CAP_RDS_CAPTURE 0x00000100 /* RDS data capture */ #define V4L2_CAP_VIDEO_OUTPUT_OVERLAY 0x00000200 /* Can do video output overlay */ +#define V4L2_CAP_HW_FREQ_SEEK 0x00000400 /* Can do hardware frequency seek */ #define V4L2_CAP_TUNER 0x00010000 /* has a tuner */ #define V4L2_CAP_AUDIO 0x00020000 /* has audio support */ @@ -1156,6 +1157,14 @@ struct v4l2_frequency __u32 reserved[8]; }; +struct v4l2_hw_freq_seek { + __u32 tuner; + enum v4l2_tuner_type type; + __u32 seek_upward; + __u32 wrap_around; + __u32 reserved[8]; +}; + /* * A U D I O */ @@ -1441,6 +1450,7 @@ struct v4l2_chip_ident { #define VIDIOC_G_CHIP_IDENT _IOWR ('V', 81, struct v4l2_chip_ident) #endif +#define VIDIOC_S_HW_FREQ_SEEK _IOW ('V', 82, struct v4l2_hw_freq_seek) #ifdef __OLD_VIDIOC_ /* for compatibility, will go away some day */ diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index 859f7a6f6f67..53b651ac685a 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -306,6 +306,8 @@ struct video_device /* Log status ioctl */ int (*vidioc_log_status) (struct file *file, void *fh); + int (*vidioc_s_hw_freq_seek) (struct file *file, void *fh, + struct v4l2_hw_freq_seek *a); /* Debugging ioctls */ #ifdef CONFIG_VIDEO_ADV_DEBUG -- cgit v1.2.3 From 6a7eba24e4f0ff725d33159f6265e3a79d53a833 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 30 Jun 2008 15:50:11 -0300 Subject: V4L/DVB (8157): gspca: all subdrivers - remaning subdrivers added - remove the decoding helper and some specific frame decodings Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/gspca.txt | 306 ++--- drivers/media/video/Makefile | 1 + drivers/media/video/gspca/Kconfig | 2 +- drivers/media/video/gspca/Makefile | 26 +- drivers/media/video/gspca/conex.c | 1059 +++++++++++++++++ drivers/media/video/gspca/etoms.c | 1062 +++++++++++++++++ drivers/media/video/gspca/gspca.c | 579 ++------- drivers/media/video/gspca/gspca.h | 39 +- drivers/media/video/gspca/mars.c | 455 +++++++ drivers/media/video/gspca/ov519.c | 2174 ++++++++++++++++++++++++++++++++++ drivers/media/video/gspca/pac207.c | 10 +- drivers/media/video/gspca/pac7311.c | 754 ++++++++++++ drivers/media/video/gspca/sonixb.c | 879 ++++++++++++++ drivers/media/video/gspca/sonixj.c | 1629 +++++++++++++++++++++++++ drivers/media/video/gspca/spca500.c | 1195 +++++++++++++++++++ drivers/media/video/gspca/spca501.c | 2219 +++++++++++++++++++++++++++++++++++ drivers/media/video/gspca/spca505.c | 933 +++++++++++++++ drivers/media/video/gspca/spca506.c | 830 +++++++++++++ drivers/media/video/gspca/spca508.c | 1774 ++++++++++++++++++++++++++++ drivers/media/video/gspca/spca561.c | 1025 ++++++++++++++++ drivers/media/video/gspca/stk014.c | 170 ++- drivers/media/video/gspca/sunplus.c | 1638 ++++++++++++++++++++++++++ drivers/media/video/gspca/t613.c | 1013 ++++++++++++++++ drivers/media/video/gspca/tv8532.c | 709 +++++++++++ drivers/media/video/gspca/vc032x.c | 1816 ++++++++++++++++++++++++++++ drivers/media/video/gspca/zc3xx.c | 649 +++++----- include/linux/videodev2.h | 2 + 27 files changed, 21964 insertions(+), 984 deletions(-) create mode 100644 drivers/media/video/gspca/conex.c create mode 100644 drivers/media/video/gspca/etoms.c create mode 100644 drivers/media/video/gspca/mars.c create mode 100644 drivers/media/video/gspca/ov519.c create mode 100644 drivers/media/video/gspca/pac7311.c create mode 100644 drivers/media/video/gspca/sonixb.c create mode 100644 drivers/media/video/gspca/sonixj.c create mode 100644 drivers/media/video/gspca/spca500.c create mode 100644 drivers/media/video/gspca/spca501.c create mode 100644 drivers/media/video/gspca/spca505.c create mode 100644 drivers/media/video/gspca/spca506.c create mode 100644 drivers/media/video/gspca/spca508.c create mode 100644 drivers/media/video/gspca/spca561.c create mode 100644 drivers/media/video/gspca/sunplus.c create mode 100644 drivers/media/video/gspca/t613.c create mode 100644 drivers/media/video/gspca/tv8532.c create mode 100644 drivers/media/video/gspca/vc032x.c (limited to 'include/linux') diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt index 9c404b56dbb3..37996e59d516 100644 --- a/Documentation/video4linux/gspca.txt +++ b/Documentation/video4linux/gspca.txt @@ -1,4 +1,4 @@ -Here the list of the known working cameras with gspca. +List of the webcams know by gspca. The modules are: gspca_main main driver @@ -6,106 +6,73 @@ The modules are: xxxx vend:prod ---- -conex 0572:0041 Creative Notebook cx11646 -etoms 102c:6151 Qcam Sangha CIF -etoms 102c:6251 Qcam xxxxxx VGA -mars 093a:050f Mars-Semi Pc-Camera +spca501 0000:0000 MystFromOri Unknow Camera +spca501 040a:0002 Kodak DVC-325 +spca500 040a:0300 Kodak EZ200 +zc3xx 041e:041e Creative WebCam Live! +spca500 041e:400a Creative PC-CAM 300 +sunplus 041e:400b Creative PC-CAM 600 +sunplus 041e:4012 PC-Cam350 +sunplus 041e:4013 Creative Pccam750 +zc3xx 041e:4017 Creative Webcam Mobile PD1090 +spca508 041e:4018 Creative Webcam Vista (PD1100) +spca561 041e:401a Creative Webcam Vista (PD1100) +zc3xx 041e:401c Creative NX +spca505 041e:401d Creative Webcam NX ULTRA +zc3xx 041e:401e Creative Nx Pro +zc3xx 041e:401f Creative Webcam Notebook PD1171 +pac207 041e:4028 Creative Webcam Vista Plus +zc3xx 041e:4029 Creative WebCam Vista Pro +zc3xx 041e:4034 Creative Instant P0620 +zc3xx 041e:4035 Creative Instant P0620D +zc3xx 041e:4036 Creative Live ! +zc3xx 041e:403a Creative Nx Pro 2 +spca561 041e:403b Creative Webcam Vista (VF0010) +zc3xx 041e:4051 Creative Live!Cam Notebook Pro (VF0250) ov519 041e:4052 Creative Live! VISTA IM +zc3xx 041e:4053 Creative Live!Cam Video IM ov519 041e:405f Creative Live! VISTA VF0330 ov519 041e:4060 Creative Live! VISTA VF0350 ov519 041e:4061 Creative Live! VISTA VF0400 ov519 041e:4064 Creative Live! VISTA VF0420 ov519 041e:4068 Creative Live! VISTA VF0470 -ov519 045e:028c Micro$oft xbox cam -ov519 054c:0154 Sonny toy4 -ov519 054c:0155 Sonny toy5 -ov519 05a9:0519 OmniVision -ov519 05a9:4519 OmniVision -ov519 05a9:8519 OmniVision -ov519 05a9:0530 OmniVision -pac207 041e:4028 Creative Webcam Vista Plus -pac207 093a:2460 PAC207 Qtec Webcam 100 -pac207 093a:2463 Philips spc200nc pac207 -pac207 093a:2464 Labtec Webcam 1200 -pac207 093a:2468 PAC207 -pac207 093a:2470 Genius GF112 -pac207 093a:2471 PAC207 Genius VideoCam ge111 -pac207 093a:2472 PAC207 Genius VideoCam ge110 -pac7311 093a:2600 PAC7311 Typhoon -pac7311 093a:2601 PAC7311 Phillips SPC610NC -pac7311 093a:2603 PAC7312 -pac7311 093a:2608 PAC7311 Trust WB-3300p -pac7311 093a:260e PAC7311 Gigaware VGA PC Camera, Trust WB-3350p, SIGMA cam 2350 -pac7311 093a:260f PAC7311 SnakeCam -sonixb 0c45:6001 Genius VideoCAM NB -sonixb 0c45:6005 Sweex Tas5110 -sonixb 0c45:6007 Sonix sn9c101 + Tas5110D -sonixb 0c45:6009 spcaCam@120 -sonixb 0c45:600d spcaCam@120 -sonixb 0c45:6011 MAX Webcam (Microdia - OV6650 - SN9C101G) -sonixb 0c45:6019 Generic Sonix OV7630 -sonixb 0c45:6024 Generic Sonix Tas5130c -sonixb 0c45:6025 Xcam Shanga -sonixb 0c45:6028 Sonix Btc Pc380 -sonixb 0c45:6029 spcaCam@150 -sonixb 0c45:602c Generic Sonix OV7630 -sonixb 0c45:602d LIC-200 LG -sonixb 0c45:602e Genius VideoCam Messenger +spca561 0458:7004 Genius VideoCAM Express V2 +sunplus 0458:7006 Genius Dsc 1.3 Smart +zc3xx 0458:7007 Genius VideoCam V2 +zc3xx 0458:700c Genius VideoCam V3 +zc3xx 0458:700f Genius VideoCam Web V2 sonixj 0458:7025 Genius Eye 311Q sonixj 045e:00f5 MicroSoft VX3000 sonixj 045e:00f7 MicroSoft VX1000 -sonixj 0471:0327 Philips SPC 600 NC -sonixj 0471:0328 Philips SPC 700 NC -sonixj 0471:0330 Philips SPC 710NC -sonixj 0c45:6040 Speed NVC 350K -sonixj 0c45:607c Sonix sn9c102p Hv7131R -sonixj 0c45:60c0 Sangha Sn535 -sonixj 0c45:60ec SN9C105+MO4000 -sonixj 0c45:60fb Surfer NoName -sonixj 0c45:60fc LG-LIC300 -sonixj 0c45:612a Avant Camera -sonixj 0c45:612c Typhoon Rasy Cam 1.3MPix -sonixj 0c45:6130 Sonix Pccam -sonixj 0c45:6138 Sn9c120 Mo4000 -sonixj 0c45:613b Surfer SN-206 -sonixj 0c45:613c Sonix Pccam168 -spca500 040a:0300 Kodak EZ200 -spca500 041e:400a Creative PC-CAM 300 +ov519 045e:028c Micro$oft xbox cam +spca508 0461:0815 Micro Innovation IC200 +zc3xx 0461:0a00 MicroInnovation WebCam320 spca500 046d:0890 Logitech QuickCam traveler +vc032x 046d:0892 Logitech Orbicam +vc032x 046d:0896 Logitech Orbicam +zc3xx 046d:08a0 Logitech QC IM +zc3xx 046d:08a1 Logitech QC IM 0x08A1 +sound +zc3xx 046d:08a2 Labtec Webcam Pro +zc3xx 046d:08a3 Logitech QC Chat +zc3xx 046d:08a6 Logitech QCim +zc3xx 046d:08a7 Logitech QuickCam Image +zc3xx 046d:08a9 Logitech Notebook Deluxe +zc3xx 046d:08aa Labtec Webcam Notebook +zc3xx 046d:08ac Logitech QuickCam Cool +zc3xx 046d:08ad Logitech QCCommunicate STX +zc3xx 046d:08ae Logitech QuickCam for Notebooks +zc3xx 046d:08af Logitech QuickCam Cool +zc3xx 046d:08b9 Logitech QC IM ??? +zc3xx 046d:08d7 Logitech QCam STX +zc3xx 046d:08d9 Logitech QuickCam IM/Connect +zc3xx 046d:08d8 Logitech Notebook Deluxe +zc3xx 046d:08da Logitech QuickCam Messenger +zc3xx 046d:08dd Logitech QuickCam for Notebooks spca500 046d:0900 Logitech Inc. ClickSmart 310 spca500 046d:0901 Logitech Inc. ClickSmart 510 -spca500 04a5:300c Benq DC1016 -spca500 04fc:7333 PalmPixDC85 -spca500 055f:c200 Mustek Gsmart 300 -spca500 055f:c220 Gsmart Mini -spca500 06bd:0404 Agfa CL20 -spca500 06be:0800 Optimedia -spca500 084d:0003 D-Link DSC-350 -spca500 08ca:0103 Aiptek PocketDV -spca500 2899:012c Toptro Industrial -spca500 8086:0630 Intel Pocket PC Camera -spca501 040a:0002 Kodak DVC-325 -spca501 0497:c001 Smile International -spca501 0506:00df 3Com HomeConnect Lite -spca501 0733:0401 Intel Create and Share -spca501 0733:0402 ViewQuest M318B -spca501 1776:501c Arowana 300K CMOS Camera -spca501 0000:0000 MystFromOri Unknow Camera -spca505 041e:401d Creative Webcam NX ULTRA -spca505 0733:0430 Intel PC Camera Pro -spca506 06e1:a190 ADS Instant VCD -spca506 0734:043b 3DeMon USB Capture aka -spca506 99fa:8988 Grandtec V.cap -spca506 99fa:8988 Grandtec V.cap -spca508 041e:4018 Creative Webcam Vista (PD1100) -spca508 0461:0815 Micro Innovation IC200 -spca508 0733:0110 ViewQuest VQ110 -spca508 0af9:0010 Hama USB Sightcam 100 -spca508 0af9:0011 Hama USB Sightcam 100 -spca508 8086:0110 Intel Easy PC Camera -spca561 041e:401a Creative Webcam Vista (PD1100) -spca561 041e:403b Creative Webcam Vista (VF0010) -spca561 0458:7004 Genius VideoCAM Express V2 +sunplus 046d:0905 Logitech ClickSmart 820 +tv8532 046d:0920 QC Express +tv8532 046d:0921 Labtec Webcam spca561 046d:0928 Logitech QC Express Etch2 spca561 046d:0929 Labtec Webcam Elch2 spca561 046d:092a Logitech QC for Notebook @@ -114,33 +81,42 @@ spca561 046d:092c Logitech QC chat Elch2 spca561 046d:092d Logitech QC Elch2 spca561 046d:092e Logitech QC Elch2 spca561 046d:092f Logitech QC Elch2 -spca561 04fc:0561 Flexcam 100 -spca561 060b:a001 Maxell Compact Pc PM3 -spca561 10fd:7e50 FlyCam Usb 100 -spca561 abcd:cdee Petcam -stk014 05e1:0893 Syntek DV4000 -sunplus 041e:400b Creative PC-CAM 600 -sunplus 041e:4012 PC-Cam350 -sunplus 041e:4013 Creative Pccam750 -sunplus 0458:7006 Genius Dsc 1.3 Smart -sunplus 046d:0905 Logitech ClickSmart 820 sunplus 046d:0960 Logitech ClickSmart 420 sunplus 0471:0322 Philips DMVC1300K +zc3xx 0471:0325 Philips SPC 200 NC +zc3xx 0471:0326 Philips SPC 300 NC +sonixj 0471:0327 Philips SPC 600 NC +sonixj 0471:0328 Philips SPC 700 NC +zc3xx 0471:032d Philips spc210nc +zc3xx 0471:032e Philips spc315nc +sonixj 0471:0330 Philips SPC 710NC +spca501 0497:c001 Smile International sunplus 04a5:3003 Benq DC 1300 sunplus 04a5:3008 Benq DC 1500 sunplus 04a5:300a Benq DC3410 +spca500 04a5:300c Benq DC1016 sunplus 04f1:1001 JVC GC A50 +spca561 04fc:0561 Flexcam 100 sunplus 04fc:500c Sunplus CA500C sunplus 04fc:504a Aiptek Mini PenCam 1.3 sunplus 04fc:504b Maxell MaxPocket LE 1.3 sunplus 04fc:5330 Digitrex 2110 sunplus 04fc:5360 Sunplus Generic +spca500 04fc:7333 PalmPixDC85 sunplus 04fc:ffff Pure DigitalDakota +spca501 0506:00df 3Com HomeConnect Lite sunplus 052b:1513 Megapix V4 +tv8532 0545:808b Veo Stingray +tv8532 0545:8333 Veo Stingray sunplus 0546:3155 Polaroid PDC3070 sunplus 0546:3191 Polaroid Ion 80 sunplus 0546:3273 Polaroid PDC2030 +ov519 054c:0154 Sonny toy4 +ov519 054c:0155 Sonny toy5 +zc3xx 055f:c005 Mustek Wcam300A +spca500 055f:c200 Mustek Gsmart 300 sunplus 055f:c211 Kowa Bs888e Microcamera +spca500 055f:c220 Gsmart Mini sunplus 055f:c230 Mustek Digicam 330K sunplus 055f:c232 Mustek MDC3500 sunplus 055f:c360 Mustek DV4000 Mpeg4 @@ -152,14 +128,34 @@ sunplus 055f:c530 Mustek Gsmart LCD 3 sunplus 055f:c540 Gsmart D30 sunplus 055f:c630 Mustek MDC4000 sunplus 055f:c650 Mustek MDC5500Z +zc3xx 055f:d003 Mustek WCam300A +zc3xx 055f:d004 Mustek WCam300 AN +conex 0572:0041 Creative Notebook cx11646 +ov519 05a9:0519 OmniVision +ov519 05a9:0530 OmniVision +ov519 05a9:4519 OmniVision +ov519 05a9:8519 OmniVision sunplus 05da:1018 Digital Dream Enigma 1.3 +stk014 05e1:0893 Syntek DV4000 +spca561 060b:a001 Maxell Compact Pc PM3 +zc3xx 0698:2003 CTX M730V built in +spca500 06bd:0404 Agfa CL20 +spca500 06be:0800 Optimedia sunplus 06d6:0031 Trust 610 LCD PowerC@m Zoom +spca506 06e1:a190 ADS Instant VCD +spca508 0733:0110 ViewQuest VQ110 +spca501 0733:0401 Intel Create and Share +spca501 0733:0402 ViewQuest M318B +spca505 0733:0430 Intel PC Camera Pro sunplus 0733:1311 Digital Dream Epsilon 1.3 sunplus 0733:1314 Mercury 2.1MEG Deluxe Classic Cam sunplus 0733:2211 Jenoptik jdc 21 LCD sunplus 0733:2221 Mercury Digital Pro 3.1p sunplus 0733:3261 Concord 3045 spca536a sunplus 0733:3281 Cyberpix S550V +spca506 0734:043b 3DeMon USB Capture aka +spca500 084d:0003 D-Link DSC-350 +spca500 08ca:0103 Aiptek PocketDV sunplus 08ca:0104 Aiptek PocketDVII 1.3 sunplus 08ca:0106 Aiptek Pocket DV3100+ sunplus 08ca:2008 Aiptek Mini PenCam 2 M @@ -173,66 +169,72 @@ sunplus 08ca:2028 Aiptek PocketCam4M sunplus 08ca:2040 Aiptek PocketDV4100M sunplus 08ca:2042 Aiptek PocketDV5100 sunplus 08ca:2060 Aiptek PocketDV5300 -sunplus 0d64:0303 Sunplus FashionCam DXG -tv8532 046d:0920 QC Express -tv8532 046d:0921 Labtec Webcam -tv8532 0545:808b Veo Stingray -tv8532 0545:8333 Veo Stingray tv8532 0923:010f ICM532 cams -vc032x 046d:0892 Logitech Orbicam -vc032x 046d:0896 Logitech Orbicam +mars 093a:050f Mars-Semi Pc-Camera +pac207 093a:2460 PAC207 Qtec Webcam 100 +pac207 093a:2463 Philips spc200nc pac207 +pac207 093a:2464 Labtec Webcam 1200 +pac207 093a:2468 PAC207 +pac207 093a:2470 Genius GF112 +pac207 093a:2471 PAC207 Genius VideoCam ge111 +pac207 093a:2472 PAC207 Genius VideoCam ge110 +pac7311 093a:2600 PAC7311 Typhoon +pac7311 093a:2601 PAC7311 Phillips SPC610NC +pac7311 093a:2603 PAC7312 +pac7311 093a:2608 PAC7311 Trust WB-3300p +pac7311 093a:260e PAC7311 Gigaware VGA PC Camera, Trust WB-3350p, SIGMA cam 2350 +pac7311 093a:260f PAC7311 SnakeCam +pac7311 093a:2621 PAC731x +zc3xx 0ac8:0302 Z-star Vimicro zc0302 vc032x 0ac8:0321 Vimicro generic vc0321 vc032x 0ac8:0323 Vimicro Vc0323 vc032x 0ac8:0328 A4Tech PK-130MG -vc032x 0ac8:c001 Sony embedded vimicro -vc032x 0ac8:c002 Sony embedded vimicro -vc032x 17ef:4802 Lenovo Vc0323+MI1310_SOC -zc3xx 041e:041e Creative WebCam Live! -zc3xx 041e:4017 Creative Webcam Mobile PD1090 -zc3xx 041e:401c Creative NX -zc3xx 041e:401e Creative Nx Pro -zc3xx 041e:401f Creative Webcam Notebook PD1171 -zc3xx 041e:4029 Creative WebCam Vista Pro -zc3xx 041e:4034 Creative Instant P0620 -zc3xx 041e:4035 Creative Instant P0620D -zc3xx 041e:4036 Creative Live ! -zc3xx 041e:403a Creative Nx Pro 2 -zc3xx 041e:4051 Creative Live!Cam Notebook Pro (VF0250) -zc3xx 041e:4053 Creative Live!Cam Video IM -zc3xx 0458:7007 Genius VideoCam V2 -zc3xx 0458:700c Genius VideoCam V3 -zc3xx 0458:700f Genius VideoCam Web V2 -zc3xx 0461:0a00 MicroInnovation WebCam320 -zc3xx 046d:08a0 Logitech QC IM -zc3xx 046d:08a1 Logitech QC IM 0x08A1 +sound -zc3xx 046d:08a2 Labtec Webcam Pro -zc3xx 046d:08a3 Logitech QC Chat -zc3xx 046d:08a6 Logitech QCim -zc3xx 046d:08a7 Logitech QuickCam Image -zc3xx 046d:08a9 Logitech Notebook Deluxe -zc3xx 046d:08aa Labtec Webcam Notebook -zc3xx 046d:08ac Logitech QuickCam Cool -zc3xx 046d:08ad Logitech QCCommunicate STX -zc3xx 046d:08ae Logitech QuickCam for Notebooks -zc3xx 046d:08af Logitech QuickCam Cool -zc3xx 046d:08b9 Logitech QC IM ??? -zc3xx 046d:08d7 Logitech QCam STX -zc3xx 046d:08d9 Logitech QuickCam IM/Connect -zc3xx 046d:08d8 Logitech Notebook Deluxe -zc3xx 046d:08da Logitech QuickCam Messenger -zc3xx 046d:08dd Logitech QuickCam for Notebooks -zc3xx 0471:0325 Philips SPC 200 NC -zc3xx 0471:0326 Philips SPC 300 NC -zc3xx 0471:032d Philips spc210nc -zc3xx 0471:032e Philips spc315nc -zc3xx 055f:c005 Mustek Wcam300A -zc3xx 055f:d003 Mustek WCam300A -zc3xx 055f:d004 Mustek WCam300 AN -zc3xx 0698:2003 CTX M730V built in -zc3xx 0ac8:0302 Z-star Vimicro zc0302 zc3xx 0ac8:301b Z-Star zc301b zc3xx 0ac8:303b Vimicro 0x303b zc3xx 0ac8:305b Z-star Vimicro zc0305b zc3xx 0ac8:307b Ldlc VC302+Ov7620 +vc032x 0ac8:c001 Sony embedded vimicro +vc032x 0ac8:c002 Sony embedded vimicro +spca508 0af9:0010 Hama USB Sightcam 100 +spca508 0af9:0011 Hama USB Sightcam 100 +sonixb 0c45:6001 Genius VideoCAM NB +sonixb 0c45:6005 Sweex Tas5110 +sonixb 0c45:6007 Sonix sn9c101 + Tas5110D +sonixb 0c45:6009 spcaCam@120 +sonixb 0c45:600d spcaCam@120 +sonixb 0c45:6011 MAX Webcam (Microdia - OV6650 - SN9C101G) +sonixb 0c45:6019 Generic Sonix OV7630 +sonixb 0c45:6024 Generic Sonix Tas5130c +sonixb 0c45:6025 Xcam Shanga +sonixb 0c45:6028 Sonix Btc Pc380 +sonixb 0c45:6029 spcaCam@150 +sonixb 0c45:602c Generic Sonix OV7630 +sonixb 0c45:602d LIC-200 LG +sonixb 0c45:602e Genius VideoCam Messenger +sonixj 0c45:6040 Speed NVC 350K +sonixj 0c45:607c Sonix sn9c102p Hv7131R +sonixj 0c45:60c0 Sangha Sn535 +sonixj 0c45:60ec SN9C105+MO4000 +sonixj 0c45:60fb Surfer NoName +sonixj 0c45:60fc LG-LIC300 +sonixj 0c45:612a Avant Camera +sonixj 0c45:612c Typhoon Rasy Cam 1.3MPix +sonixj 0c45:6130 Sonix Pccam +sonixj 0c45:6138 Sn9c120 Mo4000 +sonixj 0c45:613b Surfer SN-206 +sonixj 0c45:613c Sonix Pccam168 +sunplus 0d64:0303 Sunplus FashionCam DXG +etoms 102c:6151 Qcam Sangha CIF +etoms 102c:6251 Qcam xxxxxx VGA zc3xx 10fd:0128 Typhoon Webshot II USB 300k 0x0128 +spca561 10fd:7e50 FlyCam Usb 100 zc3xx 10fd:8050 Typhoon Webshot II USB 300k +spca501 1776:501c Arowana 300K CMOS Camera +t613 17a1:0128 T613/TAS5130A +vc032x 17ef:4802 Lenovo Vc0323+MI1310_SOC +pac207 2001:f115 D-Link DSB-C120 +spca500 2899:012c Toptro Industrial +spca508 8086:0110 Intel Easy PC Camera +spca500 8086:0630 Intel Pocket PC Camera +spca506 99fa:8988 Grandtec V.cap +spca561 abcd:cdee Petcam diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 2ec920dc32e0..d5f6eea7d2fd 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -117,6 +117,7 @@ obj-$(CONFIG_USB_SN9C102) += sn9c102/ obj-$(CONFIG_USB_ET61X251) += et61x251/ obj-$(CONFIG_USB_PWC) += pwc/ obj-$(CONFIG_USB_ZC0301) += zc0301/ +obj-$(CONFIG_USB_GSPCA) += gspca/ obj-$(CONFIG_USB_IBMCAM) += usbvideo/ obj-$(CONFIG_USB_KONICAWC) += usbvideo/ diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig index a04e413e1258..42b90742b40b 100644 --- a/drivers/media/video/gspca/Kconfig +++ b/drivers/media/video/gspca/Kconfig @@ -2,7 +2,7 @@ config USB_GSPCA tristate "USB GSPCA driver" depends on VIDEO_V4L2 ---help--- - Say Y here if you want support for various USB cameras. + Say Y here if you want support for various USB webcams. See for more info. diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile index d959f7771526..e68a8965297a 100644 --- a/drivers/media/video/gspca/Makefile +++ b/drivers/media/video/gspca/Makefile @@ -1,7 +1,29 @@ -obj-$(CONFIG_GSPCA) += gspca_main.o \ - gspca_pac207.o gspca_stk014.o gspca_zc3xx.o +obj-$(CONFIG_USB_GSPCA) += gspca_main.o \ + gspca_conex.o gspca_etoms.o gspca_mars.o \ + gspca_ov519.o gspca_pac207.o gspca_pac7311.o \ + gspca_sonixb.o gspca_sonixj.o gspca_spca500.o gspca_spca501.o \ + gspca_spca505.o gspca_spca506.o gspca_spca508.o gspca_spca561.o \ + gspca_sunplus.o gspca_stk014.o gspca_t613.o gspca_tv8532.o \ + gspca_vc032x.o gspca_zc3xx.o gspca_main-objs := gspca.o +gspca_conex-objs := conex.o +gspca_etoms-objs := etoms.o +gspca_mars-objs := mars.o +gspca_ov519-objs := ov519.o gspca_pac207-objs := pac207.o +gspca_pac7311-objs := pac7311.o +gspca_sonixb-objs := sonixb.o +gspca_sonixj-objs := sonixj.o +gspca_spca500-objs := spca500.o +gspca_spca501-objs := spca501.o +gspca_spca505-objs := spca505.o +gspca_spca506-objs := spca506.o +gspca_spca508-objs := spca508.o +gspca_spca561-objs := spca561.o gspca_stk014-objs := stk014.o +gspca_sunplus-objs := sunplus.o +gspca_t613-objs := t613.o +gspca_tv8532-objs := tv8532.o +gspca_vc032x-objs := vc032x.o gspca_zc3xx-objs := zc3xx.o diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c new file mode 100644 index 000000000000..b0294c9274e3 --- /dev/null +++ b/drivers/media/video/gspca/conex.c @@ -0,0 +1,1059 @@ +/* + * Connexant Cx11646 library + * Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr + * + * V4L2 by Jean-Francois Moine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "conex" + +#include "gspca.h" +#define CONEX_CAM 1 /* special JPEG header */ +#include "jpeg.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA USB Conexant Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + unsigned char brightness; + unsigned char contrast; + unsigned char colors; + + unsigned char qindex; +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 0xd4, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0x0a, + .maximum = 0x1f, + .step = 1, + .default_value = 0x0c, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define SD_COLOR 2 + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Color", + .minimum = 0, + .maximum = 7, + .step = 1, + .default_value = 3, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, +}; + +static struct cam_mode vga_mode[] = { + {V4L2_PIX_FMT_JPEG, 176, 144, 3}, + {V4L2_PIX_FMT_JPEG, 320, 240, 2}, + {V4L2_PIX_FMT_JPEG, 352, 288, 1}, + {V4L2_PIX_FMT_JPEG, 640, 480, 0}, +}; + +static void reg_r(struct usb_device *dev, + __u16 index, + __u8 *buffer, __u16 length) +{ + usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + 0, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, + index, buffer, length, + 500); + PDEBUG(D_USBI, "reg read i:%02x -> %02x", index, *buffer); +} + +static void reg_w(struct usb_device *dev, + __u16 index, + const __u8 *buffer, __u16 length) +{ + PDEBUG(D_USBO, "reg write i:%02x = %02x", index, *buffer); + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + 0, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, + index, (__u8 *) buffer, length, + 500); +} + +static const __u8 cx_sensor_init[][4] = { + {0x88, 0x11, 0x01, 0x01}, + {0x88, 0x12, 0x70, 0x01}, + {0x88, 0x0f, 0x00, 0x01}, + {0x88, 0x05, 0x01, 0x01}, + {} +}; + +static const __u8 cx11646_fw1[][3] = { + {0x00, 0x02, 0x00}, + {0x01, 0x43, 0x00}, + {0x02, 0xA7, 0x00}, + {0x03, 0x8B, 0x01}, + {0x04, 0xE9, 0x02}, + {0x05, 0x08, 0x04}, + {0x06, 0x08, 0x05}, + {0x07, 0x07, 0x06}, + {0x08, 0xE7, 0x06}, + {0x09, 0xC6, 0x07}, + {0x0A, 0x86, 0x08}, + {0x0B, 0x46, 0x09}, + {0x0C, 0x05, 0x0A}, + {0x0D, 0xA5, 0x0A}, + {0x0E, 0x45, 0x0B}, + {0x0F, 0xE5, 0x0B}, + {0x10, 0x85, 0x0C}, + {0x11, 0x25, 0x0D}, + {0x12, 0xC4, 0x0D}, + {0x13, 0x45, 0x0E}, + {0x14, 0xE4, 0x0E}, + {0x15, 0x64, 0x0F}, + {0x16, 0xE4, 0x0F}, + {0x17, 0x64, 0x10}, + {0x18, 0xE4, 0x10}, + {0x19, 0x64, 0x11}, + {0x1A, 0xE4, 0x11}, + {0x1B, 0x64, 0x12}, + {0x1C, 0xE3, 0x12}, + {0x1D, 0x44, 0x13}, + {0x1E, 0xC3, 0x13}, + {0x1F, 0x24, 0x14}, + {0x20, 0xA3, 0x14}, + {0x21, 0x04, 0x15}, + {0x22, 0x83, 0x15}, + {0x23, 0xE3, 0x15}, + {0x24, 0x43, 0x16}, + {0x25, 0xA4, 0x16}, + {0x26, 0x23, 0x17}, + {0x27, 0x83, 0x17}, + {0x28, 0xE3, 0x17}, + {0x29, 0x43, 0x18}, + {0x2A, 0xA3, 0x18}, + {0x2B, 0x03, 0x19}, + {0x2C, 0x63, 0x19}, + {0x2D, 0xC3, 0x19}, + {0x2E, 0x22, 0x1A}, + {0x2F, 0x63, 0x1A}, + {0x30, 0xC3, 0x1A}, + {0x31, 0x23, 0x1B}, + {0x32, 0x83, 0x1B}, + {0x33, 0xE2, 0x1B}, + {0x34, 0x23, 0x1C}, + {0x35, 0x83, 0x1C}, + {0x36, 0xE2, 0x1C}, + {0x37, 0x23, 0x1D}, + {0x38, 0x83, 0x1D}, + {0x39, 0xE2, 0x1D}, + {0x3A, 0x23, 0x1E}, + {0x3B, 0x82, 0x1E}, + {0x3C, 0xC3, 0x1E}, + {0x3D, 0x22, 0x1F}, + {0x3E, 0x63, 0x1F}, + {0x3F, 0xC1, 0x1F}, + {} +}; +static void cx11646_fw(struct gspca_dev*gspca_dev) +{ + __u8 val; + int i = 0; + + val = 0x02; + reg_w(gspca_dev->dev, 0x006a, &val, 1); + while (cx11646_fw1[i][1]) { + reg_w(gspca_dev->dev, 0x006b, cx11646_fw1[i], 3); + i++; + } + val = 0x00; + reg_w(gspca_dev->dev, 0x006a, &val, 1); +} + +static __u8 cxsensor[] = { + 0x88, 0x12, 0x70, 0x01, + 0x88, 0x0d, 0x02, 0x01, + 0x88, 0x0f, 0x00, 0x01, + 0x88, 0x03, 0x71, 0x01, 0x88, 0x04, 0x00, 0x01, /* 3 */ + 0x88, 0x02, 0x10, 0x01, + 0x88, 0x00, 0xD4, 0x01, 0x88, 0x01, 0x01, 0x01, /* 5 */ + 0x88, 0x0B, 0x00, 0x01, + 0x88, 0x0A, 0x0A, 0x01, + 0x88, 0x00, 0x08, 0x01, 0x88, 0x01, 0x00, 0x01, /* 8 */ + 0x88, 0x05, 0x01, 0x01, + 0xA1, 0x18, 0x00, 0x01, + 0x00 +}; + +static __u8 reg20[] = { 0x10, 0x42, 0x81, 0x19, 0xd3, 0xff, 0xa7, 0xff }; +static __u8 reg28[] = { 0x87, 0x00, 0x87, 0x00, 0x8f, 0xff, 0xea, 0xff }; +static __u8 reg10[] = { 0xb1, 0xb1 }; +static __u8 reg71a[] = { 0x08, 0x18, 0x0a, 0x1e }; /* 640 */ +static __u8 reg71b[] = { 0x04, 0x0c, 0x05, 0x0f }; + /* 352{0x04,0x0a,0x06,0x12}; //352{0x05,0x0e,0x06,0x11}; //352 */ +static __u8 reg71c[] = { 0x02, 0x07, 0x03, 0x09 }; + /* 320{0x04,0x0c,0x05,0x0f}; //320 */ +static __u8 reg71d[] = { 0x02, 0x07, 0x03, 0x09 }; /* 176 */ +static __u8 reg7b[] = { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff }; + +static void cx_sensor(struct gspca_dev*gspca_dev) +{ + __u8 val = 0; + int i = 0; + __u8 bufread[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + int length = 0; + __u8 *ptsensor = cxsensor; + + reg_w(gspca_dev->dev, 0x0020, reg20, 8); + reg_w(gspca_dev->dev, 0x0028, reg28, 8); + reg_w(gspca_dev->dev, 0x0010, reg10, 8); + val = 0x03; + reg_w(gspca_dev->dev, 0x0092, &val, 1); + + switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode) { + case 0: + reg_w(gspca_dev->dev, 0x0071, reg71a, 4); + break; + case 1: + reg_w(gspca_dev->dev, 0x0071, reg71b, 4); + break; + default: +/* case 2: */ + reg_w(gspca_dev->dev, 0x0071, reg71c, 4); + break; + case 3: + reg_w(gspca_dev->dev, 0x0071, reg71d, 4); + break; + } + reg_w(gspca_dev->dev, 0x007b, reg7b, 6); + val = 0x00; + reg_w(gspca_dev->dev, 0x00f8, &val, 1); + reg_w(gspca_dev->dev, 0x0010, reg10, 8); + val = 0x41; + reg_w(gspca_dev->dev, 0x0098, &val, 1); + for (i = 0; i < 11; i++) { + if (i == 3 || i == 5 || i == 8) + length = 8; + else + length = 4; + reg_w(gspca_dev->dev, 0x00e5, ptsensor, length); + if (length == 4) + reg_r(gspca_dev->dev, 0x00e8, &val, 1); + else + reg_r(gspca_dev->dev, 0x00e8, bufread, length); + ptsensor += length; + } + reg_r(gspca_dev->dev, 0x00e7, bufread, 8); +} + +static __u8 cx_inits_176[] = { + 0x33, 0x81, 0xB0, 0x00, 0x90, 0x00, 0x0A, 0x03, /* 176x144 */ + 0x00, 0x03, 0x03, 0x03, 0x1B, 0x05, 0x30, 0x03, + 0x65, 0x15, 0x18, 0x25, 0x03, 0x25, 0x08, 0x30, + 0x3B, 0x25, 0x10, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xDC, 0xFF, 0xEE, 0xFF, 0xC5, 0xFF, 0xBF, 0xFF, + 0xF7, 0xFF, 0x88, 0xFF, 0x66, 0x02, 0x28, 0x02, + 0x1E, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static __u8 cx_inits_320[] = { + 0x7f, 0x7f, 0x40, 0x01, 0xf0, 0x00, 0x02, 0x01, + 0x00, 0x01, 0x01, 0x01, 0x10, 0x00, 0x02, 0x01, + 0x65, 0x45, 0xfa, 0x4c, 0x2c, 0xdf, 0xb9, 0x81, + 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xe2, 0xff, 0xf1, 0xff, 0xc2, 0xff, 0xbc, 0xff, + 0xf5, 0xff, 0x6d, 0xff, 0xf6, 0x01, 0x43, 0x02, + 0xd3, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static __u8 cx_inits_352[] = { + 0x2e, 0x7c, 0x60, 0x01, 0x20, 0x01, 0x05, 0x03, + 0x00, 0x06, 0x03, 0x06, 0x1b, 0x10, 0x05, 0x3b, + 0x30, 0x25, 0x18, 0x25, 0x08, 0x30, 0x03, 0x25, + 0x3b, 0x30, 0x25, 0x1b, 0x10, 0x05, 0x00, 0x00, + 0xe3, 0xff, 0xf1, 0xff, 0xc2, 0xff, 0xbc, 0xff, + 0xf5, 0xff, 0x6b, 0xff, 0xee, 0x01, 0x43, 0x02, + 0xe4, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static __u8 cx_inits_640[] = { + 0x7e, 0x7e, 0x80, 0x02, 0xe0, 0x01, 0x01, 0x01, + 0x00, 0x02, 0x01, 0x02, 0x10, 0x30, 0x01, 0x01, + 0x65, 0x45, 0xf7, 0x52, 0x2c, 0xdf, 0xb9, 0x81, + 0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0xe2, 0xff, 0xf1, 0xff, 0xc2, 0xff, 0xbc, 0xff, + 0xf6, 0xff, 0x7b, 0xff, 0x01, 0x02, 0x43, 0x02, + 0x77, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static int cx11646_initsize(struct gspca_dev *gspca_dev) +{ + __u8 *cxinit; + __u8 val; + static const __u8 reg12[] = { 0x08, 0x05, 0x07, 0x04, 0x24 }; + static const __u8 reg17[] = + { 0x0a, 0x00, 0xf2, 0x01, 0x0f, 0x00, 0x97, 0x02 }; + + switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode) { + case 0: + cxinit = cx_inits_640; + break; + case 1: + cxinit = cx_inits_352; + break; + default: +/* case 2: */ + cxinit = cx_inits_320; + break; + case 3: + cxinit = cx_inits_176; + break; + } + val = 0x01; + reg_w(gspca_dev->dev, 0x009a, &val, 1); + val = 0x10; + reg_w(gspca_dev->dev, 0x0010, &val, 1); + reg_w(gspca_dev->dev, 0x0012, reg12, 5); + reg_w(gspca_dev->dev, 0x0017, reg17, 8); + val = 0x00; + reg_w(gspca_dev->dev, 0x00c0, &val, 1); + val = 0x04; + reg_w(gspca_dev->dev, 0x00c1, &val, 1); + val = 0x04; + reg_w(gspca_dev->dev, 0x00c2, &val, 1); + + reg_w(gspca_dev->dev, 0x0061, cxinit, 8); + cxinit += 8; + reg_w(gspca_dev->dev, 0x00ca, cxinit, 8); + cxinit += 8; + reg_w(gspca_dev->dev, 0x00d2, cxinit, 8); + cxinit += 8; + reg_w(gspca_dev->dev, 0x00da, cxinit, 6); + cxinit += 8; + reg_w(gspca_dev->dev, 0x0041, cxinit, 8); + cxinit += 8; + reg_w(gspca_dev->dev, 0x0049, cxinit, 8); + cxinit += 8; + reg_w(gspca_dev->dev, 0x0051, cxinit, 2); + + reg_r(gspca_dev->dev, 0x0010, &val, 1); + return val; +} + +static __u8 cx_jpeg_init[][8] = { + {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x15}, /* 1 */ + {0x0f, 0x10, 0x12, 0x10, 0x0d, 0x15, 0x12, 0x11}, + {0x12, 0x18, 0x16, 0x15, 0x19, 0x20, 0x35, 0x22}, + {0x20, 0x1d, 0x1d, 0x20, 0x41, 0x2e, 0x31, 0x26}, + {0x35, 0x4d, 0x43, 0x51, 0x4f, 0x4b, 0x43, 0x4a}, + {0x49, 0x55, 0x5F, 0x79, 0x67, 0x55, 0x5A, 0x73}, + {0x5B, 0x49, 0x4A, 0x6A, 0x90, 0x6B, 0x73, 0x7D}, + {0x81, 0x88, 0x89, 0x88, 0x52, 0x66, 0x95, 0xA0}, + {0x94, 0x84, 0x9E, 0x79, 0x85, 0x88, 0x83, 0x01}, + {0x15, 0x0F, 0x10, 0x12, 0x10, 0x0D, 0x15, 0x12}, + {0x11, 0x12, 0x18, 0x16, 0x15, 0x19, 0x20, 0x35}, + {0x22, 0x20, 0x1D, 0x1D, 0x20, 0x41, 0x2E, 0x31}, + {0x26, 0x35, 0x4D, 0x43, 0x51, 0x4F, 0x4B, 0x43}, + {0x4A, 0x49, 0x55, 0x5F, 0x79, 0x67, 0x55, 0x5A}, + {0x73, 0x5B, 0x49, 0x4A, 0x6A, 0x90, 0x6B, 0x73}, + {0x7D, 0x81, 0x88, 0x89, 0x88, 0x52, 0x66, 0x95}, + {0xA0, 0x94, 0x84, 0x9E, 0x79, 0x85, 0x88, 0x83}, + {0xFF, 0xC4, 0x01, 0xA2, 0x00, 0x00, 0x01, 0x05}, + {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02}, + {0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A}, + {0x0B, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01}, + {0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05}, + {0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x10, 0x00}, + {0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05}, + {0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01}, + {0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21}, + {0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22}, + {0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23}, + {0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24}, + {0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17}, + {0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29}, + {0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A}, + {0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A}, + {0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A}, + {0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A}, + {0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A}, + {0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A}, + {0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99}, + {0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8}, + {0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7}, + {0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6}, + {0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5}, + {0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3}, + {0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1}, + {0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9}, + {0xFA, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04}, + {0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01}, + {0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04}, + {0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07}, + {0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14}, + {0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33}, + {0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16}, + {0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19}, + {0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36}, + {0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46}, + {0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56}, + {0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66}, + {0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76}, + {0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85}, + {0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94}, + {0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3}, + {0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2}, + {0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA}, + {0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9}, + {0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8}, + {0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7}, + {0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6}, + {0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0x20, 0x00, 0x1F}, + {0x02, 0x0C, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x11, 0x00, 0x11, 0x22, 0x00, 0x22}, + {0x22, 0x11, 0x22, 0x22, 0x11, 0x33, 0x33, 0x11}, + {0x44, 0x66, 0x22, 0x55, 0x66, 0xFF, 0xDD, 0x00}, + {0x04, 0x00, 0x14, 0xFF, 0xC0, 0x00, 0x11, 0x08}, + {0x00, 0xF0, 0x01, 0x40, 0x03, 0x00, 0x21, 0x00}, + {0x01, 0x11, 0x01, 0x02, 0x11, 0x01, 0xFF, 0xDA}, + {0x00, 0x0C, 0x03, 0x00, 0x00, 0x01, 0x11, 0x02}, + {0x11, 0x00, 0x3F, 0x00, 0xFF, 0xD9, 0x00, 0x00} /* 79 */ +}; + + +static __u8 cxjpeg_640[][8] = { + {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x10}, /* 1 */ + {0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, 0x0d}, + {0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, 0x1a}, + {0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, 0x1d}, + {0x28, 0x3a, 0x33, 0x3D, 0x3C, 0x39, 0x33, 0x38}, + {0x37, 0x40, 0x48, 0x5C, 0x4E, 0x40, 0x44, 0x57}, + {0x45, 0x37, 0x38, 0x50, 0x6D, 0x51, 0x57, 0x5F}, + {0x62, 0x67, 0x68, 0x67, 0x3E, 0x4D, 0x71, 0x79}, + {0x70, 0x64, 0x78, 0x5C, 0x65, 0x67, 0x63, 0x01}, + {0x10, 0x0B, 0x0C, 0x0E, 0x0C, 0x0A, 0x10, 0x0E}, + {0x0D, 0x0E, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28}, + {0x1A, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25}, + {0x1D, 0x28, 0x3A, 0x33, 0x3D, 0x3C, 0x39, 0x33}, + {0x38, 0x37, 0x40, 0x48, 0x5C, 0x4E, 0x40, 0x44}, + {0x57, 0x45, 0x37, 0x38, 0x50, 0x6D, 0x51, 0x57}, + {0x5F, 0x62, 0x67, 0x68, 0x67, 0x3E, 0x4D, 0x71}, + {0x79, 0x70, 0x64, 0x78, 0x5C, 0x65, 0x67, 0x63}, + {0xFF, 0x20, 0x00, 0x1F, 0x00, 0x83, 0x00, 0x00}, + {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00}, + {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22}, + {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55}, + {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x28, 0xFF}, + {0xC0, 0x00, 0x11, 0x08, 0x01, 0xE0, 0x02, 0x80}, + {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02}, + {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00}, + {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00}, + {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} /* 27 */ +}; +static __u8 cxjpeg_352[][8] = { + {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x0d}, + {0x09, 0x09, 0x0b, 0x09, 0x08, 0x0D, 0x0b, 0x0a}, + {0x0b, 0x0e, 0x0d, 0x0d, 0x0f, 0x13, 0x1f, 0x14}, + {0x13, 0x11, 0x11, 0x13, 0x26, 0x1b, 0x1d, 0x17}, + {0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28, 0x2C}, + {0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35, 0x44}, + {0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44, 0x4A}, + {0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58, 0x5F}, + {0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D, 0x01}, + {0x0D, 0x09, 0x09, 0x0B, 0x09, 0x08, 0x0D, 0x0B}, + {0x0A, 0x0B, 0x0E, 0x0D, 0x0D, 0x0F, 0x13, 0x1F}, + {0x14, 0x13, 0x11, 0x11, 0x13, 0x26, 0x1B, 0x1D}, + {0x17, 0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28}, + {0x2C, 0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35}, + {0x44, 0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44}, + {0x4A, 0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58}, + {0x5F, 0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D}, + {0xFF, 0x20, 0x00, 0x1F, 0x01, 0x83, 0x00, 0x00}, + {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00}, + {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22}, + {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55}, + {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x16, 0xFF}, + {0xC0, 0x00, 0x11, 0x08, 0x01, 0x20, 0x01, 0x60}, + {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02}, + {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00}, + {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00}, + {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} +}; +static __u8 cxjpeg_320[][8] = { + {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x05}, + {0x03, 0x04, 0x04, 0x04, 0x03, 0x05, 0x04, 0x04}, + {0x04, 0x05, 0x05, 0x05, 0x06, 0x07, 0x0c, 0x08}, + {0x07, 0x07, 0x07, 0x07, 0x0f, 0x0b, 0x0b, 0x09}, + {0x0C, 0x11, 0x0F, 0x12, 0x12, 0x11, 0x0f, 0x11}, + {0x11, 0x13, 0x16, 0x1C, 0x17, 0x13, 0x14, 0x1A}, + {0x15, 0x11, 0x11, 0x18, 0x21, 0x18, 0x1A, 0x1D}, + {0x1D, 0x1F, 0x1F, 0x1F, 0x13, 0x17, 0x22, 0x24}, + {0x22, 0x1E, 0x24, 0x1C, 0x1E, 0x1F, 0x1E, 0x01}, + {0x05, 0x03, 0x04, 0x04, 0x04, 0x03, 0x05, 0x04}, + {0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x07, 0x0C}, + {0x08, 0x07, 0x07, 0x07, 0x07, 0x0F, 0x0B, 0x0B}, + {0x09, 0x0C, 0x11, 0x0F, 0x12, 0x12, 0x11, 0x0F}, + {0x11, 0x11, 0x13, 0x16, 0x1C, 0x17, 0x13, 0x14}, + {0x1A, 0x15, 0x11, 0x11, 0x18, 0x21, 0x18, 0x1A}, + {0x1D, 0x1D, 0x1F, 0x1F, 0x1F, 0x13, 0x17, 0x22}, + {0x24, 0x22, 0x1E, 0x24, 0x1C, 0x1E, 0x1F, 0x1E}, + {0xFF, 0x20, 0x00, 0x1F, 0x02, 0x0C, 0x00, 0x00}, + {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00}, + {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22}, + {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55}, + {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x14, 0xFF}, + {0xC0, 0x00, 0x11, 0x08, 0x00, 0xF0, 0x01, 0x40}, + {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02}, + {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00}, + {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00}, + {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} /* 27 */ +}; +static __u8 cxjpeg_176[][8] = { + {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x0d}, + {0x09, 0x09, 0x0B, 0x09, 0x08, 0x0D, 0x0B, 0x0A}, + {0x0B, 0x0E, 0x0D, 0x0D, 0x0F, 0x13, 0x1F, 0x14}, + {0x13, 0x11, 0x11, 0x13, 0x26, 0x1B, 0x1D, 0x17}, + {0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28, 0x2C}, + {0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35, 0x44}, + {0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44, 0x4A}, + {0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58, 0x5F}, + {0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D, 0x01}, + {0x0D, 0x09, 0x09, 0x0B, 0x09, 0x08, 0x0D, 0x0B}, + {0x0A, 0x0B, 0x0E, 0x0D, 0x0D, 0x0F, 0x13, 0x1F}, + {0x14, 0x13, 0x11, 0x11, 0x13, 0x26, 0x1B, 0x1D}, + {0x17, 0x1F, 0x2D, 0x28, 0x30, 0x2F, 0x2D, 0x28}, + {0x2C, 0x2B, 0x32, 0x38, 0x48, 0x3D, 0x32, 0x35}, + {0x44, 0x36, 0x2B, 0x2C, 0x3F, 0x55, 0x3F, 0x44}, + {0x4A, 0x4D, 0x50, 0x51, 0x50, 0x30, 0x3C, 0x58}, + {0x5F, 0x58, 0x4E, 0x5E, 0x48, 0x4F, 0x50, 0x4D}, + {0xFF, 0x20, 0x00, 0x1F, 0x03, 0xA1, 0x00, 0x00}, + {0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00}, + {0x11, 0x22, 0x00, 0x22, 0x22, 0x11, 0x22, 0x22}, + {0x11, 0x33, 0x33, 0x11, 0x44, 0x66, 0x22, 0x55}, + {0x66, 0xFF, 0xDD, 0x00, 0x04, 0x00, 0x0B, 0xFF}, + {0xC0, 0x00, 0x11, 0x08, 0x00, 0x90, 0x00, 0xB0}, + {0x03, 0x00, 0x21, 0x00, 0x01, 0x11, 0x01, 0x02}, + {0x11, 0x01, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x00}, + {0x00, 0x01, 0x11, 0x02, 0x11, 0x00, 0x3F, 0x00}, + {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} +}; +static __u8 cxjpeg_qtable[][8] = { /* 640 take with the zcx30x part */ + {0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x08}, + {0x06, 0x06, 0x07, 0x06, 0x05, 0x08, 0x07, 0x07}, + {0x07, 0x09, 0x09, 0x08, 0x0a, 0x0c, 0x14, 0x0a}, + {0x0c, 0x0b, 0x0b, 0x0c, 0x19, 0x12, 0x13, 0x0f}, + {0x14, 0x1d, 0x1a, 0x1f, 0x1e, 0x1d, 0x1a, 0x1c}, + {0x1c, 0x20, 0x24, 0x2e, 0x27, 0x20, 0x22, 0x2c}, + {0x23, 0x1c, 0x1c, 0x28, 0x37, 0x29, 0x2c, 0x30}, + {0x31, 0x34, 0x34, 0x34, 0x1f, 0x27, 0x39, 0x3d}, + {0x38, 0x32, 0x3c, 0x2e, 0x33, 0x34, 0x32, 0x01}, + {0x09, 0x09, 0x09, 0x0c, 0x0b, 0x0c, 0x18, 0x0a}, + {0x0a, 0x18, 0x32, 0x21, 0x1c, 0x21, 0x32, 0x32}, + {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, + {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, + {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, + {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, + {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, + {0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32}, + {0xFF, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} /* 18 */ +}; + + +static void cx11646_jpegInit(struct gspca_dev*gspca_dev) +{ + __u8 val; + int i; + int length; + + val = 0x01; + reg_w(gspca_dev->dev, 0x00c0, &val, 1); + val = 0x00; + reg_w(gspca_dev->dev, 0x00c3, &val, 1); + val = 0x00; + reg_w(gspca_dev->dev, 0x00c0, &val, 1); + reg_r(gspca_dev->dev, 0x0001, &val, 1); + length = 8; + for (i = 0; i < 79; i++) { + if (i == 78) + length = 6; + reg_w(gspca_dev->dev, 0x0008, cx_jpeg_init[i], length); + } + reg_r(gspca_dev->dev, 0x0002, &val, 1); + val = 0x14; + reg_w(gspca_dev->dev, 0x0055, &val, 1); +} + +static __u8 reg12[] = { 0x0a, 0x05, 0x07, 0x04, 0x19 }; +static __u8 regE5_8[] = { 0x88, 0x00, 0xd4, 0x01, 0x88, 0x01, 0x01, 0x01 }; +static __u8 regE5a[] = { 0x88, 0x0a, 0x0c, 0x01 }; +static __u8 regE5b[] = { 0x88, 0x0b, 0x12, 0x01 }; +static __u8 regE5c[] = { 0x88, 0x05, 0x01, 0x01 }; +static __u8 reg51[] = { 0x77, 0x03 }; +static __u8 reg70 = 0x03; + +static void cx11646_jpeg(struct gspca_dev*gspca_dev) +{ + __u8 val; + int i; + int length = 8; + __u8 Reg55 = 0x14; + __u8 bufread[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + int retry = 50; + + val = 0x01; + reg_w(gspca_dev->dev, 0x00c0, &val, 1); + val = 0x00; + reg_w(gspca_dev->dev, 0x00c3, &val, 1); + val = 0x00; + reg_w(gspca_dev->dev, 0x00c0, &val, 1); + reg_r(gspca_dev->dev, 0x0001, &val, 1); + switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode) { + case 0: + for (i = 0; i < 27; i++) { + if (i == 26) + length = 2; + reg_w(gspca_dev->dev, 0x0008, + cxjpeg_640[i], length); + } + Reg55 = 0x28; + break; + case 1: + for (i = 0; i < 27; i++) { + if (i == 26) + length = 2; + reg_w(gspca_dev->dev, 0x0008, + cxjpeg_352[i], length); + } + Reg55 = 0x16; + break; + default: +/* case 2: */ + for (i = 0; i < 27; i++) { + if (i == 26) + length = 2; + reg_w(gspca_dev->dev, 0x0008, + cxjpeg_320[i], length); + } + Reg55 = 0x14; + break; + case 3: + for (i = 0; i < 27; i++) { + if (i == 26) + length = 2; + reg_w(gspca_dev->dev, 0x0008, + cxjpeg_176[i], length); + } + Reg55 = 0x0B; + break; + } + + reg_r(gspca_dev->dev, 0x0002, &val, 1); + val = Reg55; + reg_w(gspca_dev->dev, 0x0055, &val, 1); + reg_r(gspca_dev->dev, 0x0002, &val, 1); + reg_w(gspca_dev->dev, 0x0010, reg10, 2); + val = 0x02; + reg_w(gspca_dev->dev, 0x0054, &val, 1); + val = 0x01; + reg_w(gspca_dev->dev, 0x0054, &val, 1); + val = 0x94; + reg_w(gspca_dev->dev, 0x0000, &val, 1); + val = 0xc0; + reg_w(gspca_dev->dev, 0x0053, &val, 1); + val = 0xe1; + reg_w(gspca_dev->dev, 0x00fc, &val, 1); + val = 0x00; + reg_w(gspca_dev->dev, 0x0000, &val, 1); + /* wait for completion */ + while (retry--) { + reg_r(gspca_dev->dev, 0x0002, &val, 1); + /* 0x07 until 0x00 */ + if (val == 0x00) + break; + val = 0x00; + reg_w(gspca_dev->dev, 0x0053, &val, 1); + } + if (retry == 0) + PDEBUG(D_ERR, "Damned Errors sending jpeg Table"); + /* send the qtable now */ + reg_r(gspca_dev->dev, 0x0001, &val, 1); /* -> 0x18 */ + length = 8; + for (i = 0; i < 18; i++) { + if (i == 17) + length = 2; + reg_w(gspca_dev->dev, 0x0008, + cxjpeg_qtable[i], length); + + } + reg_r(gspca_dev->dev, 0x0002, &val, 1); /* 0x00 */ + reg_r(gspca_dev->dev, 0x0053, &val, 1); /* 0x00 */ + val = 0x02; + reg_w(gspca_dev->dev, 0x0054, &val, 1); + val = 0x01; + reg_w(gspca_dev->dev, 0x0054, &val, 1); + val = 0x94; + reg_w(gspca_dev->dev, 0x0000, &val, 1); + val = 0xc0; + reg_w(gspca_dev->dev, 0x0053, &val, 1); + + reg_r(gspca_dev->dev, 0x0038, &val, 1); /* 0x40 */ + reg_r(gspca_dev->dev, 0x0038, &val, 1); /* 0x40 */ + reg_r(gspca_dev->dev, 0x001f, &val, 1); /* 0x38 */ + reg_w(gspca_dev->dev, 0x0012, reg12, 5); + reg_w(gspca_dev->dev, 0x00e5, regE5_8, 8); + reg_r(gspca_dev->dev, 0x00e8, bufread, 8); + reg_w(gspca_dev->dev, 0x00e5, regE5a, 4); + reg_r(gspca_dev->dev, 0x00e8, &val, 1); /* 0x00 */ + val = 0x01; + reg_w(gspca_dev->dev, 0x009a, &val, 1); + reg_w(gspca_dev->dev, 0x00e5, regE5b, 4); + reg_r(gspca_dev->dev, 0x00e8, &val, 1); /* 0x00 */ + reg_w(gspca_dev->dev, 0x00e5, regE5c, 4); + reg_r(gspca_dev->dev, 0x00e8, &val, 1); /* 0x00 */ + + reg_w(gspca_dev->dev, 0x0051, reg51, 2); + reg_w(gspca_dev->dev, 0x0010, reg10, 2); + reg_w(gspca_dev->dev, 0x0070, ®70, 1); +} + +static void cx11646_init1(struct gspca_dev *gspca_dev) +{ + __u8 val; + int i = 0; + + val = 0; + reg_w(gspca_dev->dev, 0x0010, &val, 1); + reg_w(gspca_dev->dev, 0x0053, &val, 1); + reg_w(gspca_dev->dev, 0x0052, &val, 1); + val = 0x2f; + reg_w(gspca_dev->dev, 0x009b, &val, 1); + val = 0x10; + reg_w(gspca_dev->dev, 0x009c, &val, 1); + reg_r(gspca_dev->dev, 0x0098, &val, 1); + val = 0x40; + reg_w(gspca_dev->dev, 0x0098, &val, 1); + reg_r(gspca_dev->dev, 0x0099, &val, 1); + val = 0x07; + reg_w(gspca_dev->dev, 0x0099, &val, 1); + val = 0x40; + reg_w(gspca_dev->dev, 0x0039, &val, 1); + val = 0xff; + reg_w(gspca_dev->dev, 0x003c, &val, 1); + val = 0x1f; + reg_w(gspca_dev->dev, 0x003f, &val, 1); + val = 0x40; + reg_w(gspca_dev->dev, 0x003d, &val, 1); +/* val= 0x60; */ +/* reg_w(gspca_dev->dev,0x00,0x00,0x003d,&val,1); */ + reg_r(gspca_dev->dev, 0x0099, &val, 1); /* ->0x07 */ + + while (cx_sensor_init[i][0]) { + reg_w(gspca_dev->dev, 0x00e5, cx_sensor_init[i], 1); + reg_r(gspca_dev->dev, 0x00e8, &val, 1); /* -> 0x00 */ + if (i == 1) { + val = 1; + reg_w(gspca_dev->dev, 0x00ed, &val, 1); + reg_r(gspca_dev->dev, 0x00ed, &val, 1); /* -> 0x01 */ + } + i++; + } + val = 0x00; + reg_w(gspca_dev->dev, 0x00c3, &val, 1); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + cam->cam_mode = vga_mode; + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + + sd->qindex = 0; /* set the quantization table */ + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; + sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value; + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + cx11646_init1(gspca_dev); + cx11646_initsize(gspca_dev); + cx11646_fw(gspca_dev); + cx_sensor(gspca_dev); + cx11646_jpegInit(gspca_dev); + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + cx11646_initsize(gspca_dev); + cx11646_fw(gspca_dev); + cx_sensor(gspca_dev); + cx11646_jpeg(gspca_dev); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + int retry = 50; + __u8 val; + + val = 0; + reg_w(gspca_dev->dev, 0x0000, &val, 1); + reg_r(gspca_dev->dev, 0x0002, &val, 1); + val = 0; + reg_w(gspca_dev->dev, 0x0053, &val, 1); + + while (retry--) { +/* reg_r (gspca_dev->dev,0x00,0x00,0x0002,&val,1);*/ + reg_r(gspca_dev->dev, 0x0053, &val, 1); + if (val == 0) + break; + } + val = 0; + reg_w(gspca_dev->dev, 0x0000, &val, 1); + reg_r(gspca_dev->dev, 0x0002, &val, 1); + + val = 0; + reg_w(gspca_dev->dev, 0x0010, &val, 1); + reg_r(gspca_dev->dev, 0x0033, &val, 1); + val = 0xe0; + reg_w(gspca_dev->dev, 0x00fc, &val, 1); +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + unsigned char *data, /* isoc packet */ + int len) /* iso packet length */ +{ + if (data[0] == 0xff && data[1] == 0xd8) { + + /* start of frame */ + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, 0); + + /* put the JPEG header in the new frame */ + jpeg_put_header(gspca_dev, frame, + ((struct sd *) gspca_dev)->qindex, + 0x22); + data += 2; + len -= 2; + } + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); +} + +static void setbrightness(struct gspca_dev*gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 regE5cbx[] = { 0x88, 0x00, 0xd4, 0x01, 0x88, 0x01, 0x01, 0x01 }; + __u8 reg51c[] = { 0x77, 0x03 }; + __u8 bright; + __u8 colors; + __u8 val; + __u8 bufread[8]; + + bright = sd->brightness; + colors = sd->colors; + regE5cbx[2] = bright; + reg51c[1] = colors; + reg_w(gspca_dev->dev, 0x00e5, regE5cbx, 8); + reg_r(gspca_dev->dev, 0x00e8, bufread, 8); + reg_w(gspca_dev->dev, 0x00e5, regE5c, 4); + reg_r(gspca_dev->dev, 0x00e8, &val, 1); /* 0x00 */ + + reg_w(gspca_dev->dev, 0x0051, reg51c, 2); + reg_w(gspca_dev->dev, 0x0010, reg10, 2); + reg_w(gspca_dev->dev, 0x0070, ®70, 1); +} + +static void setcontrast(struct gspca_dev*gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 regE5acx[] = { 0x88, 0x0a, 0x0c, 0x01 }; /* seem MSB */ + /* __u8 regE5bcx[]={0x88,0x0b,0x12,0x01}; // LSB */ + __u8 reg51c[] = { 0x77, 0x03 }; + __u8 val; + + reg51c[1] = sd->colors; + regE5acx[2] = sd->contrast; + reg_w(gspca_dev->dev, 0x00e5, regE5acx, 4); + reg_r(gspca_dev->dev, 0x00e8, &val, 1); /* 0x00 */ + reg_w(gspca_dev->dev, 0x0051, reg51c, 2); + reg_w(gspca_dev->dev, 0x0010, reg10, 2); + reg_w(gspca_dev->dev, 0x0070, ®70, 1); +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) { + setbrightness(gspca_dev); + setcontrast(gspca_dev); + } + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->colors; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x0572, 0x0041), DVNM("Creative Notebook cx11646")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/etoms.c b/drivers/media/video/gspca/etoms.c new file mode 100644 index 000000000000..c479f638413e --- /dev/null +++ b/drivers/media/video/gspca/etoms.c @@ -0,0 +1,1062 @@ +/* + * Etoms Et61x151 GPL Linux driver by Michel Xhaard (09/09/2004) + * + * V4L2 by Jean-Francois Moine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "etoms" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("Etoms USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + unsigned char brightness; + unsigned char contrast; + unsigned char colors; + unsigned char autogain; + + char sensor; +#define SENSOR_PAS106 0 +#define SENSOR_TAS5130CXX 1 + signed char ag_cnt; +#define AG_CNT_START 13 +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 1, + .maximum = 127, + .step = 1, + .default_value = 63, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define SD_COLOR 2 + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Color", + .minimum = 0, + .maximum = 15, + .step = 1, + .default_value = 7, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, +#define SD_AUTOGAIN 3 + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +}; + +static struct cam_mode vga_mode[] = { + {V4L2_PIX_FMT_SBGGR8, 320, 240, 1}, +/* {V4L2_PIX_FMT_SBGGR8, 640, 480, 0}, */ +}; + +static struct cam_mode sif_mode[] = { + {V4L2_PIX_FMT_SBGGR8, 176, 144, 1}, + {V4L2_PIX_FMT_SBGGR8, 352, 288, 0}, +}; + +#define ETOMS_ALT_SIZE_1000 12 + +#define ET_GPIO_DIR_CTRL 0x04 /* Control IO bit[0..5] (0 in 1 out) */ +#define ET_GPIO_OUT 0x05 /* Only IO data */ +#define ET_GPIO_IN 0x06 /* Read Only IO data */ +#define ET_RESET_ALL 0x03 +#define ET_ClCK 0x01 +#define ET_CTRL 0x02 /* enable i2c OutClck Powerdown mode */ + +#define ET_COMP 0x12 /* Compression register */ +#define ET_MAXQt 0x13 +#define ET_MINQt 0x14 +#define ET_COMP_VAL0 0x02 +#define ET_COMP_VAL1 0x03 + +#define ET_REG1d 0x1d +#define ET_REG1e 0x1e +#define ET_REG1f 0x1f +#define ET_REG20 0x20 +#define ET_REG21 0x21 +#define ET_REG22 0x22 +#define ET_REG23 0x23 +#define ET_REG24 0x24 +#define ET_REG25 0x25 +/* base registers for luma calculation */ +#define ET_LUMA_CENTER 0x39 + +#define ET_G_RED 0x4d +#define ET_G_GREEN1 0x4e +#define ET_G_BLUE 0x4f +#define ET_G_GREEN2 0x50 +#define ET_G_GR_H 0x51 +#define ET_G_GB_H 0x52 + +#define ET_O_RED 0x34 +#define ET_O_GREEN1 0x35 +#define ET_O_BLUE 0x36 +#define ET_O_GREEN2 0x37 + +#define ET_SYNCHRO 0x68 +#define ET_STARTX 0x69 +#define ET_STARTY 0x6a +#define ET_WIDTH_LOW 0x6b +#define ET_HEIGTH_LOW 0x6c +#define ET_W_H_HEIGTH 0x6d + +#define ET_REG6e 0x6e /* OBW */ +#define ET_REG6f 0x6f /* OBW */ +#define ET_REG70 0x70 /* OBW_AWB */ +#define ET_REG71 0x71 /* OBW_AWB */ +#define ET_REG72 0x72 /* OBW_AWB */ +#define ET_REG73 0x73 /* Clkdelay ns */ +#define ET_REG74 0x74 /* test pattern */ +#define ET_REG75 0x75 /* test pattern */ + +#define ET_I2C_CLK 0x8c +#define ET_PXL_CLK 0x60 + +#define ET_I2C_BASE 0x89 +#define ET_I2C_COUNT 0x8a +#define ET_I2C_PREFETCH 0x8b +#define ET_I2C_REG 0x88 +#define ET_I2C_DATA7 0x87 +#define ET_I2C_DATA6 0x86 +#define ET_I2C_DATA5 0x85 +#define ET_I2C_DATA4 0x84 +#define ET_I2C_DATA3 0x83 +#define ET_I2C_DATA2 0x82 +#define ET_I2C_DATA1 0x81 +#define ET_I2C_DATA0 0x80 + +#define PAS106_REG2 0x02 /* pxlClk = systemClk/(reg2) */ +#define PAS106_REG3 0x03 /* line/frame H [11..4] */ +#define PAS106_REG4 0x04 /* line/frame L [3..0] */ +#define PAS106_REG5 0x05 /* exposure time line offset(default 5) */ +#define PAS106_REG6 0x06 /* exposure time pixel offset(default 6) */ +#define PAS106_REG7 0x07 /* signbit Dac (default 0) */ +#define PAS106_REG9 0x09 +#define PAS106_REG0e 0x0e /* global gain [4..0](default 0x0e) */ +#define PAS106_REG13 0x13 /* end i2c write */ + +static __u8 GainRGBG[] = { 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 }; + +static __u8 I2c2[] = { 0x08, 0x08, 0x08, 0x08, 0x0d }; + +static __u8 I2c3[] = { 0x12, 0x05 }; + +static __u8 I2c4[] = { 0x41, 0x08 }; + +static void Et_RegRead(struct usb_device *dev, + __u16 index, __u8 *buffer, int len) +{ + usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + 0, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0, index, buffer, len, 500); +} + +static void Et_RegWrite(struct usb_device *dev, + __u16 index, __u8 *buffer, __u16 len) +{ + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + 0, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0, index, buffer, len, 500); +} + +static int Et_i2cwrite(struct usb_device *dev, __u8 reg, __u8 * buffer, + __u16 length, __u8 mode) +{ +/* buffer should be [D0..D7] */ + int i, j; + __u8 base = 0x40; /* sensor base for the pas106 */ + __u8 ptchcount = 0; + + ptchcount = (((length & 0x07) << 4) | (mode & 0x03)); +/* set the base address */ + Et_RegWrite(dev, ET_I2C_BASE, &base, 1); +/* set count and prefetch */ + Et_RegWrite(dev, ET_I2C_COUNT, &ptchcount, 1); +/* set the register base */ + Et_RegWrite(dev, ET_I2C_REG, ®, 1); + j = length - 1; + for (i = 0; i < length; i++) { + Et_RegWrite(dev, (ET_I2C_DATA0 + j), &buffer[j], 1); + j--; + } + return 0; +} + +static int Et_i2cread(struct usb_device *dev, __u8 reg, __u8 * buffer, + __u16 length, __u8 mode) +{ +/* buffer should be [D0..D7] */ + int i, j; + __u8 base = 0x40; /* sensor base for the pas106 */ + __u8 ptchcount; + __u8 prefetch = 0x02; + + ptchcount = (((length & 0x07) << 4) | (mode & 0x03)); +/* set the base address */ + Et_RegWrite(dev, ET_I2C_BASE, &base, 1); +/* set count and prefetch */ + Et_RegWrite(dev, ET_I2C_COUNT, &ptchcount, 1); +/* set the register base */ + Et_RegWrite(dev, ET_I2C_REG, ®, 1); + Et_RegWrite(dev, ET_I2C_PREFETCH, &prefetch, 1); + prefetch = 0x00; + Et_RegWrite(dev, ET_I2C_PREFETCH, &prefetch, 1); + j = length - 1; + for (i = 0; i < length; i++) { + Et_RegRead(dev, (ET_I2C_DATA0 + j), &buffer[j], 1); + j--; + } + return 0; +} + +static int Et_WaitStatus(struct usb_device *dev) +{ + __u8 bytereceived; + int retry = 10; + + while (retry--) { + Et_RegRead(dev, ET_ClCK, &bytereceived, 1); + if (bytereceived != 0) + return 1; + } + return 0; +} + +static int Et_videoOff(struct usb_device *dev) +{ + int err; + __u8 stopvideo = 0; + + Et_RegWrite(dev, ET_GPIO_OUT, &stopvideo, 1); + err = Et_WaitStatus(dev); + if (!err) + PDEBUG(D_ERR, "timeout Et_waitStatus VideoON"); + return err; +} + +static int Et_videoOn(struct usb_device *dev) +{ + int err; + __u8 startvideo = 0x10; /* set Bit5 */ + + Et_RegWrite(dev, ET_GPIO_OUT, &startvideo, 1); + err = Et_WaitStatus(dev); + if (!err) + PDEBUG(D_ERR, "timeout Et_waitStatus VideoOFF"); + return err; +} + +static void Et_init2(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 value = 0x00; + __u8 received = 0x00; + __u8 FormLine[] = { 0x84, 0x03, 0x14, 0xf4, 0x01, 0x05 }; + + PDEBUG(D_STREAM, "Open Init2 ET"); + value = 0x2f; + Et_RegWrite(dev, ET_GPIO_DIR_CTRL, &value, 1); + value = 0x10; + Et_RegWrite(dev, ET_GPIO_OUT, &value, 1); + Et_RegRead(dev, ET_GPIO_IN, &received, 1); + value = 0x14; /* 0x14 // 0x16 enabled pattern */ + Et_RegWrite(dev, ET_ClCK, &value, 1); + value = 0x1b; + Et_RegWrite(dev, ET_CTRL, &value, 1); + + /* compression et subsampling */ + if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode) + value = ET_COMP_VAL1; /* 320 */ + else + value = ET_COMP_VAL0; /* 640 */ + Et_RegWrite(dev, ET_COMP, &value, 1); + value = 0x1f; + Et_RegWrite(dev, ET_MAXQt, &value, 1); + value = 0x04; + Et_RegWrite(dev, ET_MINQt, &value, 1); + /* undocumented registers */ + value = 0xff; + Et_RegWrite(dev, ET_REG1d, &value, 1); + value = 0xff; + Et_RegWrite(dev, ET_REG1e, &value, 1); + value = 0xff; + Et_RegWrite(dev, ET_REG1f, &value, 1); + value = 0x35; + Et_RegWrite(dev, ET_REG20, &value, 1); + value = 0x01; + Et_RegWrite(dev, ET_REG21, &value, 1); + value = 0x00; + Et_RegWrite(dev, ET_REG22, &value, 1); + value = 0xff; + Et_RegWrite(dev, ET_REG23, &value, 1); + value = 0xff; + Et_RegWrite(dev, ET_REG24, &value, 1); + value = 0x0f; + Et_RegWrite(dev, ET_REG25, &value, 1); + /* colors setting */ + value = 0x11; + Et_RegWrite(dev, 0x30, &value, 1); /* 0x30 */ + value = 0x40; + Et_RegWrite(dev, 0x31, &value, 1); + value = 0x00; + Et_RegWrite(dev, 0x32, &value, 1); + value = 0x00; + Et_RegWrite(dev, ET_O_RED, &value, 1); /* 0x34 */ + value = 0x00; + Et_RegWrite(dev, ET_O_GREEN1, &value, 1); + value = 0x00; + Et_RegWrite(dev, ET_O_BLUE, &value, 1); + value = 0x00; + Et_RegWrite(dev, ET_O_GREEN2, &value, 1); + /*************/ + value = 0x80; + Et_RegWrite(dev, ET_G_RED, &value, 1); /* 0x4d */ + value = 0x80; + Et_RegWrite(dev, ET_G_GREEN1, &value, 1); + value = 0x80; + Et_RegWrite(dev, ET_G_BLUE, &value, 1); + value = 0x80; + Et_RegWrite(dev, ET_G_GREEN2, &value, 1); + value = 0x00; + Et_RegWrite(dev, ET_G_GR_H, &value, 1); + value = 0x00; + Et_RegWrite(dev, ET_G_GB_H, &value, 1); /* 0x52 */ + /* Window control registers */ + + value = 0x80; /* use cmc_out */ + Et_RegWrite(dev, 0x61, &value, 1); + + value = 0x02; + Et_RegWrite(dev, 0x62, &value, 1); + value = 0x03; + Et_RegWrite(dev, 0x63, &value, 1); + value = 0x14; + Et_RegWrite(dev, 0x64, &value, 1); + value = 0x0e; + Et_RegWrite(dev, 0x65, &value, 1); + value = 0x02; + Et_RegWrite(dev, 0x66, &value, 1); + value = 0x02; + Et_RegWrite(dev, 0x67, &value, 1); + + /**************************************/ + value = 0x8f; + Et_RegWrite(dev, ET_SYNCHRO, &value, 1); /* 0x68 */ + value = 0x69; /* 0x6a //0x69 */ + Et_RegWrite(dev, ET_STARTX, &value, 1); + value = 0x0d; /* 0x0d //0x0c */ + Et_RegWrite(dev, ET_STARTY, &value, 1); + value = 0x80; + Et_RegWrite(dev, ET_WIDTH_LOW, &value, 1); + value = 0xe0; + Et_RegWrite(dev, ET_HEIGTH_LOW, &value, 1); + value = 0x60; + Et_RegWrite(dev, ET_W_H_HEIGTH, &value, 1); /* 6d */ + value = 0x86; + Et_RegWrite(dev, ET_REG6e, &value, 1); + value = 0x01; + Et_RegWrite(dev, ET_REG6f, &value, 1); + value = 0x26; + Et_RegWrite(dev, ET_REG70, &value, 1); + value = 0x7a; + Et_RegWrite(dev, ET_REG71, &value, 1); + value = 0x01; + Et_RegWrite(dev, ET_REG72, &value, 1); + /* Clock Pattern registers ***************** */ + value = 0x00; + Et_RegWrite(dev, ET_REG73, &value, 1); + value = 0x18; /* 0x28 */ + Et_RegWrite(dev, ET_REG74, &value, 1); + value = 0x0f; /* 0x01 */ + Et_RegWrite(dev, ET_REG75, &value, 1); + /**********************************************/ + value = 0x20; + Et_RegWrite(dev, 0x8a, &value, 1); + value = 0x0f; + Et_RegWrite(dev, 0x8d, &value, 1); + value = 0x08; + Et_RegWrite(dev, 0x8e, &value, 1); + /**************************************/ + value = 0x08; + Et_RegWrite(dev, 0x03, &value, 1); + value = 0x03; + Et_RegWrite(dev, ET_PXL_CLK, &value, 1); + value = 0xff; + Et_RegWrite(dev, 0x81, &value, 1); + value = 0x00; + Et_RegWrite(dev, 0x80, &value, 1); + value = 0xff; + Et_RegWrite(dev, 0x81, &value, 1); + value = 0x20; + Et_RegWrite(dev, 0x80, &value, 1); + value = 0x01; + Et_RegWrite(dev, 0x03, &value, 1); + value = 0x00; + Et_RegWrite(dev, 0x03, &value, 1); + value = 0x08; + Et_RegWrite(dev, 0x03, &value, 1); + /********************************************/ + + /* Et_RegRead(dev,0x0,ET_I2C_BASE,&received,1); + always 0x40 as the pas106 ??? */ + /* set the sensor */ + if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode) { + value = 0x04; /* 320 */ + Et_RegWrite(dev, ET_PXL_CLK, &value, 1); + /* now set by fifo the FormatLine setting */ + Et_RegWrite(dev, 0x62, FormLine, 6); + } else { /* 640 */ + /* setting PixelClock + 0x03 mean 24/(3+1) = 6 Mhz + 0x05 -> 24/(5+1) = 4 Mhz + 0x0b -> 24/(11+1) = 2 Mhz + 0x17 -> 24/(23+1) = 1 Mhz + */ + value = 0x1e; /* 0x17 */ + Et_RegWrite(dev, ET_PXL_CLK, &value, 1); + /* now set by fifo the FormatLine setting */ + Et_RegWrite(dev, 0x62, FormLine, 6); + } + + /* set exposure times [ 0..0x78] 0->longvalue 0x78->shortvalue */ + value = 0x47; /* 0x47; */ + Et_RegWrite(dev, 0x81, &value, 1); + value = 0x40; /* 0x40; */ + Et_RegWrite(dev, 0x80, &value, 1); + /* Pedro change */ + /* Brightness change Brith+ decrease value */ + /* Brigth- increase value */ + /* original value = 0x70; */ + value = 0x30; /* 0x20; */ + Et_RegWrite(dev, 0x81, &value, 1); /* set brightness */ + value = 0x20; /* 0x20; */ + Et_RegWrite(dev, 0x80, &value, 1); +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + static __u8 I2cc[] = { 0x05, 0x02, 0x02, 0x05, 0x0d }; + __u8 i2cflags = 0x01; + /* __u8 green = 0; */ + __u8 colors = sd->colors; + + I2cc[3] = colors; /* red */ + I2cc[0] = 15 - colors; /* blue */ + /* green = 15 - ((((7*I2cc[0]) >> 2 ) + I2cc[3]) >> 1); */ + /* I2cc[1] = I2cc[2] = green; */ + if (sd->sensor == SENSOR_PAS106) { + Et_i2cwrite(dev, PAS106_REG13, &i2cflags, 1, 3); + Et_i2cwrite(dev, PAS106_REG9, I2cc, sizeof(I2cc), 1); + } +/* PDEBUG(D_CONF , "Etoms red %d blue %d green %d", + I2cc[3], I2cc[0], green); */ +} + +static void getcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + /* __u8 valblue = 0; */ + __u8 valred; + + if (sd->sensor == SENSOR_PAS106) { + /* Et_i2cread(gspca_dev->dev,PAS106_REG9,&valblue,1,1); */ + Et_i2cread(gspca_dev->dev, PAS106_REG9 + 3, &valred, 1, 1); + sd->colors = valred & 0x0f; + } +} + +static void Et_init1(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 value = 0x00; + __u8 received = 0x00; +/* __u8 I2c0 [] ={0x0a,0x12,0x05,0x22,0xac,0x00,0x01,0x00}; */ + __u8 I2c0[] = { 0x0a, 0x12, 0x05, 0x6d, 0xcd, 0x00, 0x01, 0x00 }; + /* try 1/120 0x6d 0xcd 0x40 */ +/* __u8 I2c0 [] ={0x0a,0x12,0x05,0xfe,0xfe,0xc0,0x01,0x00}; + * 1/60000 hmm ?? */ + + PDEBUG(D_STREAM, "Open Init1 ET"); + value = 7; + Et_RegWrite(dev, ET_GPIO_DIR_CTRL, &value, 1); + Et_RegRead(dev, ET_GPIO_IN, &received, 1); + value = 1; + Et_RegWrite(dev, ET_RESET_ALL, &value, 1); + value = 0; + Et_RegWrite(dev, ET_RESET_ALL, &value, 1); + value = 0x10; + Et_RegWrite(dev, ET_ClCK, &value, 1); + value = 0x19; + Et_RegWrite(dev, ET_CTRL, &value, 1); + /* compression et subsampling */ + if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode) + value = ET_COMP_VAL1; + else + value = ET_COMP_VAL0; + + PDEBUG(D_STREAM, "Open mode %d Compression %d", + gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode, + value); + Et_RegWrite(dev, ET_COMP, &value, 1); + value = 0x1d; + Et_RegWrite(dev, ET_MAXQt, &value, 1); + value = 0x02; + Et_RegWrite(dev, ET_MINQt, &value, 1); + /* undocumented registers */ + value = 0xff; + Et_RegWrite(dev, ET_REG1d, &value, 1); + value = 0xff; + Et_RegWrite(dev, ET_REG1e, &value, 1); + value = 0xff; + Et_RegWrite(dev, ET_REG1f, &value, 1); + value = 0x35; + Et_RegWrite(dev, ET_REG20, &value, 1); + value = 0x01; + Et_RegWrite(dev, ET_REG21, &value, 1); + value = 0x00; + Et_RegWrite(dev, ET_REG22, &value, 1); + value = 0xf7; + Et_RegWrite(dev, ET_REG23, &value, 1); + value = 0xff; + Et_RegWrite(dev, ET_REG24, &value, 1); + value = 0x07; + Et_RegWrite(dev, ET_REG25, &value, 1); + /* colors setting */ + value = 0x80; + Et_RegWrite(dev, ET_G_RED, &value, 1); + value = 0x80; + Et_RegWrite(dev, ET_G_GREEN1, &value, 1); + value = 0x80; + Et_RegWrite(dev, ET_G_BLUE, &value, 1); + value = 0x80; + Et_RegWrite(dev, ET_G_GREEN2, &value, 1); + value = 0x00; + Et_RegWrite(dev, ET_G_GR_H, &value, 1); + value = 0x00; + Et_RegWrite(dev, ET_G_GB_H, &value, 1); + /* Window control registers */ + value = 0xf0; + Et_RegWrite(dev, ET_SYNCHRO, &value, 1); + value = 0x56; /* 0x56 */ + Et_RegWrite(dev, ET_STARTX, &value, 1); + value = 0x05; /* 0x04 */ + Et_RegWrite(dev, ET_STARTY, &value, 1); + value = 0x60; + Et_RegWrite(dev, ET_WIDTH_LOW, &value, 1); + value = 0x20; + Et_RegWrite(dev, ET_HEIGTH_LOW, &value, 1); + value = 0x50; + Et_RegWrite(dev, ET_W_H_HEIGTH, &value, 1); + value = 0x86; + Et_RegWrite(dev, ET_REG6e, &value, 1); + value = 0x01; + Et_RegWrite(dev, ET_REG6f, &value, 1); + value = 0x86; + Et_RegWrite(dev, ET_REG70, &value, 1); + value = 0x14; + Et_RegWrite(dev, ET_REG71, &value, 1); + value = 0x00; + Et_RegWrite(dev, ET_REG72, &value, 1); + /* Clock Pattern registers */ + value = 0x00; + Et_RegWrite(dev, ET_REG73, &value, 1); + value = 0x00; + Et_RegWrite(dev, ET_REG74, &value, 1); + value = 0x0a; + Et_RegWrite(dev, ET_REG75, &value, 1); + value = 0x04; + Et_RegWrite(dev, ET_I2C_CLK, &value, 1); + value = 0x01; + Et_RegWrite(dev, ET_PXL_CLK, &value, 1); + /* set the sensor */ + if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode) { + I2c0[0] = 0x06; + Et_i2cwrite(dev, PAS106_REG2, I2c0, sizeof(I2c0), 1); + Et_i2cwrite(dev, PAS106_REG9, I2c2, sizeof(I2c2), 1); + value = 0x06; + Et_i2cwrite(dev, PAS106_REG2, &value, 1, 1); + Et_i2cwrite(dev, PAS106_REG3, I2c3, sizeof(I2c3), 1); + /* value = 0x1f; */ + value = 0x04; + Et_i2cwrite(dev, PAS106_REG0e, &value, 1, 1); + } else { + I2c0[0] = 0x0a; + + Et_i2cwrite(dev, PAS106_REG2, I2c0, sizeof(I2c0), 1); + Et_i2cwrite(dev, PAS106_REG9, I2c2, sizeof(I2c2), 1); + value = 0x0a; + + Et_i2cwrite(dev, PAS106_REG2, &value, 1, 1); + Et_i2cwrite(dev, PAS106_REG3, I2c3, sizeof(I2c3), 1); + value = 0x04; + /* value = 0x10; */ + Et_i2cwrite(dev, PAS106_REG0e, &value, 1, 1); + /* bit 2 enable bit 1:2 select 0 1 2 3 + value = 0x07; * curve 0 * + Et_i2cwrite(dev,PAS106_REG0f,&value,1,1); + */ + } + +/* value = 0x01; */ +/* value = 0x22; */ +/* Et_i2cwrite(dev, PAS106_REG5, &value, 1, 1); */ + /* magnetude and sign bit for DAC */ + Et_i2cwrite(dev, PAS106_REG7, I2c4, sizeof I2c4, 1); + /* now set by fifo the whole colors setting */ + Et_RegWrite(dev, ET_G_RED, GainRGBG, 6); + getcolors(gspca_dev); + setcolors(gspca_dev); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + __u16 vendor; + __u16 product; + + vendor = id->idVendor; + product = id->idProduct; +/* switch (vendor) { */ +/* case 0x102c: * Etoms */ + switch (product) { + case 0x6151: + sd->sensor = SENSOR_PAS106; /* Etoms61x151 */ + break; + case 0x6251: + sd->sensor = SENSOR_TAS5130CXX; /* Etoms61x251 */ + break; +/* } */ +/* break; */ + } + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 1; + if (sd->sensor == SENSOR_PAS106) { + cam->cam_mode = sif_mode; + cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; + } else { + cam->cam_mode = vga_mode; + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + } + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; + sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value; + sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value; + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + int err; + __u8 value; + + PDEBUG(D_STREAM, "Initialize ET1"); + if (sd->sensor == SENSOR_PAS106) + Et_init1(gspca_dev); + else + Et_init2(gspca_dev); + value = 0x08; + Et_RegWrite(dev, ET_RESET_ALL, &value, 1); + err = Et_videoOff(dev); + PDEBUG(D_STREAM, "Et_Init_VideoOff %d", err); + return 0; +} + +/* -- start the camera -- */ +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + int err; + __u8 value; + + if (sd->sensor == SENSOR_PAS106) + Et_init1(gspca_dev); + else + Et_init2(gspca_dev); + + value = 0x08; + Et_RegWrite(dev, ET_RESET_ALL, &value, 1); + err = Et_videoOn(dev); + PDEBUG(D_STREAM, "Et_VideoOn %d", err); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + int err; + + err = Et_videoOff(gspca_dev->dev); + PDEBUG(D_STREAM, "Et_VideoOff %d", err); + +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + __u8 brightness = sd->brightness; + + for (i = 0; i < 4; i++) + Et_RegWrite(gspca_dev->dev, (ET_O_RED + i), &brightness, 1); +} + +static void getbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + int brightness = 0; + __u8 value = 0; + + for (i = 0; i < 4; i++) { + Et_RegRead(gspca_dev->dev, (ET_O_RED + i), &value, 1); + brightness += value; + } + sd->brightness = brightness >> 3; +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 RGBG[] = { 0x80, 0x80, 0x80, 0x80, 0x00, 0x00 }; + __u8 contrast = sd->contrast; + + memset(RGBG, contrast, sizeof RGBG - 2); + Et_RegWrite(gspca_dev->dev, ET_G_RED, RGBG, 6); +} + +static void getcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + int contrast = 0; + __u8 value = 0; + + for (i = 0; i < 4; i++) { + Et_RegRead(gspca_dev->dev, (ET_G_RED + i), &value, 1); + contrast += value; + } + sd->contrast = contrast >> 2; +} + +static __u8 Et_getgainG(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 value = 0; + + if (sd->sensor == SENSOR_PAS106) { + Et_i2cread(gspca_dev->dev, PAS106_REG0e, &value, 1, 1); + PDEBUG(D_CONF, "Etoms gain G %d", value); + return value; + } + return 0x1f; +} + +static void Et_setgainG(struct gspca_dev *gspca_dev, __u8 gain) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + __u8 i2cflags = 0x01; + + if (sd->sensor == SENSOR_PAS106) { + Et_i2cwrite(dev, PAS106_REG13, &i2cflags, 1, 3); + Et_i2cwrite(dev, PAS106_REG0e, &gain, 1, 1); + } +} + +#define BLIMIT(bright) \ + (__u8)((bright > 0x1f)?0x1f:((bright < 4)?3:bright)) +#define LIMIT(color) \ + (unsigned char)((color > 0xff)?0xff:((color < 0)?0:color)) + +static void setautogain(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 GRBG[] = { 0, 0, 0, 0 }; + __u8 luma = 0; + __u8 luma_mean = 128; + __u8 luma_delta = 20; + __u8 spring = 4; + int Gbright = 0; + __u8 r, g, b; + + Gbright = Et_getgainG(gspca_dev); + Et_RegRead(dev, ET_LUMA_CENTER, GRBG, 4); + g = (GRBG[0] + GRBG[3]) >> 1; + r = GRBG[1]; + b = GRBG[2]; + r = ((r << 8) - (r << 4) - (r << 3)) >> 10; + b = ((b << 7) >> 10); + g = ((g << 9) + (g << 7) + (g << 5)) >> 10; + luma = LIMIT(r + g + b); + PDEBUG(D_FRAM, "Etoms luma G %d", luma); + if (luma < luma_mean - luma_delta || luma > luma_mean + luma_delta) { + Gbright += (luma_mean - luma) >> spring; + Gbright = BLIMIT(Gbright); + PDEBUG(D_FRAM, "Etoms Gbright %d", Gbright); + Et_setgainG(gspca_dev, (__u8) Gbright); + } +} + +#undef BLIMIT +#undef LIMIT + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + unsigned char *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd; + int seqframe; + + seqframe = data[0] & 0x3f; + len = (int) (((data[0] & 0xc0) << 2) | data[1]); + if (seqframe == 0x3f) { + PDEBUG(D_FRAM, + "header packet found datalength %d !!", len); + PDEBUG(D_FRAM, "G %d R %d G %d B %d", + data[2], data[3], data[4], data[5]); + data += 30; + /* don't change datalength as the chips provided it */ + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len); + sd = (struct sd *) gspca_dev; + if (sd->ag_cnt >= 0) { + if (--sd->ag_cnt < 0) { + sd->ag_cnt = AG_CNT_START; + setautogain(gspca_dev); + } + } + return; + } + if (len) { + data += 8; + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + } else { /* Drop Packet */ + gspca_dev->last_packet_type = DISCARD_PACKET; + } +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcontrast(gspca_dev); + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcolors(gspca_dev); + *val = sd->colors; + return 0; +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + if (val) + sd->ag_cnt = AG_CNT_START; + else + sd->ag_cnt = -1; + return 0; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x102c, 0x6151), DVNM("Qcam Sangha CIF")}, + {USB_DEVICE(0x102c, 0x6251), DVNM("Qcam xxxxxx VGA")}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} + +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 423ebbdc4b4f..5583c53e4863 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -35,24 +35,24 @@ #include "gspca.h" -/* option */ -#define GSPCA_HLP 0 +#undef CONFIG_VIDEO_V4L1_COMPAT /* global values */ #define DEF_NURBS 2 /* default number of URBs (mmap) */ +#define USR_NURBS 5 /* default number of URBs (userptr) */ MODULE_AUTHOR("Jean-Francois Moine "); MODULE_DESCRIPTION("GSPCA USB Camera Driver"); MODULE_LICENSE("GPL"); -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(0, 2, 15) -static const char version[] = "0.2.15"; +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; static int video_nr = -1; static int comp_fac = 30; /* Buffer size ratio when compressed in % */ -#ifdef GSPCA_DEBUG +#ifdef VIDEO_ADV_DEBUG int gspca_debug = D_ERR | D_PROBE; EXPORT_SYMBOL(gspca_debug); @@ -81,224 +81,7 @@ static void PDEBUG_MODE(char *txt, __u32 pixfmt, int w, int h) #define GSPCA_MEMORY_NO 0 /* V4L2_MEMORY_xxx starts from 1 */ #define GSPCA_MEMORY_READ 7 -#ifndef GSPCA_HLP #define BUF_ALL_FLAGS (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE) -#else -#define GSPCA_BUF_FLAG_DECODE 0x1000 /* internal buffer flag */ -#define BUF_ALL_FLAGS (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE \ - | GSPCA_BUF_FLAG_DECODE) - -static int autostart = 4; -module_param(autostart, int, 0644); -MODULE_PARM_DESC(autostart, - "Automatically start the helper process"); - -/* try to start the helper process */ -static void start_hlp(void) -{ - int ret; - static char *argv[] = {"gspca_hlp", NULL}; - static char *env[] = {NULL}; - - if (autostart <= 0) { - if (autostart < 0) - PDEBUG(D_ERR|D_PROBE, "Too many helper restart"); - return; - } - autostart--; - if (autostart == 0) - autostart = -1; - ret = call_usermodehelper("/sbin/gspca_hlp", argv, env, - UMH_WAIT_EXEC); - if (ret != 0) - PDEBUG(D_ERR|D_PROBE, - "/sbin/gspca_hlp start failed %d", ret); -} - -/* /dev/gspca_hlp stuff */ -#include -#include "gspca_hlp.h" - -/* !! possible decodings defined in decoder.c */ -static __u32 bayer_to_tb[] = { - V4L2_PIX_FMT_SBGGR8, - V4L2_PIX_FMT_YUYV, - V4L2_PIX_FMT_YUV420, - V4L2_PIX_FMT_RGB24, - V4L2_PIX_FMT_BGR24, - V4L2_PIX_FMT_RGB565, -}; -static __u32 jpeg_to_tb[] = { - V4L2_PIX_FMT_JPEG, - V4L2_PIX_FMT_YUYV, - V4L2_PIX_FMT_YUV420, - V4L2_PIX_FMT_RGB24, - V4L2_PIX_FMT_BGR24, - V4L2_PIX_FMT_RGB565, -}; - -/* /dev/gspca_hlp device */ -struct hlp_dev { - struct gspca_dev *gspca_dev; /* associated device */ - struct gspca_frame *frame; /* frame being decoded */ - __u32 pixfmt; /* webcam pixel format */ - atomic_t nevent; /* nb of frames ready to decode */ - wait_queue_head_t wq; /* wait queue */ - char fr_d; /* next frame to decode */ -} *hlp; - -static int hlp_open(struct inode *inode, struct file *file) -{ - struct hlp_dev *hlp_dev; - - PDEBUG(D_CONF, "hlp open"); - if (hlp != 0) - return -EBUSY; - hlp_dev = kzalloc(sizeof *hlp_dev, GFP_KERNEL); - if (hlp_dev == NULL) { - err("couldn't kzalloc hlp struct"); - return -EIO; - } - init_waitqueue_head(&hlp_dev->wq); - file->private_data = hlp_dev; - hlp = hlp_dev; - return 0; -} - -static int hlp_close(struct inode *inode, struct file *file) -{ - struct gspca_dev *gspca_dev; - int mode; - - PDEBUG(D_CONF, "hlp close"); - file->private_data = NULL; - - /* stop decoding */ - gspca_dev = hlp->gspca_dev; - if (gspca_dev != 0) { - mode = gspca_dev->curr_mode; - gspca_dev->pixfmt = gspca_dev->cam.cam_mode[mode].pixfmt; - } - - /* destroy the helper structure */ - kfree(hlp); - hlp = 0; - - /* try to restart the helper process */ - start_hlp(); - return 0; -} - -static ssize_t hlp_read(struct file *file, char __user *buf, - size_t cnt, loff_t *ppos) -{ - struct hlp_dev *hlp_dev = file->private_data; - struct gspca_dev *gspca_dev; - struct gspca_frame *frame; - struct gspca_hlp_read_hd head; - int i, j, len, ret; - - PDEBUG(D_FRAM, "hlp read (%d)", cnt); - - /* check / wait till a frame is ready */ - for (;;) { - gspca_dev = hlp_dev->gspca_dev; - if (gspca_dev != 0 && gspca_dev->streaming) { - i = hlp_dev->fr_d; /* frame to decode */ - j = gspca_dev->fr_queue[i]; - frame = &gspca_dev->frame[j]; - if (frame->v4l2_buf.flags & GSPCA_BUF_FLAG_DECODE) - break; - } - ret = wait_event_interruptible(hlp_dev->wq, - atomic_read(&hlp_dev->nevent) > 0); - if (ret < 0) { /* helper process is killed */ - autostart = 0; /* don't restart it */ - return ret; - } - } - atomic_dec(&hlp_dev->nevent); - hlp_dev->fr_d = (i + 1) % gspca_dev->nframes; - PDEBUG(D_FRAM, "hlp read q:%d i:%d d:%d o:%d", - gspca_dev->fr_q, - gspca_dev->fr_i, - hlp_dev->fr_d, - gspca_dev->fr_o); - - hlp_dev->frame = frame; /* memorize the current frame */ - len = frame->v4l2_buf.bytesused; - if (cnt < sizeof head - sizeof head.data + len) -/*fixme: special errno?*/ - return -EINVAL; - head.pixfmt_out = gspca_dev->pixfmt; - head.pixfmt_in = hlp_dev->pixfmt; - head.width = gspca_dev->width; - head.height = gspca_dev->height; - copy_to_user(buf, &head, sizeof head); - copy_to_user(buf + sizeof head - sizeof head.data, - frame->data, len); - return sizeof head - sizeof head.data + len; -} - -static ssize_t hlp_write(struct file *file, - const char __user *buf, - size_t cnt, loff_t *ppos) -{ - struct hlp_dev *hlp_dev = file->private_data; - struct gspca_dev *gspca_dev; - struct gspca_frame *frame; - - PDEBUG(D_FRAM, "hlp write (%d)", cnt); - gspca_dev = hlp_dev->gspca_dev; - if (gspca_dev == 0) - return cnt; - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) - return -ERESTARTSYS; - if (!gspca_dev->streaming) - goto out; - frame = hlp_dev->frame; - hlp_dev->frame = 0; - if (frame == 0) - goto out; - if (cnt > frame->v4l2_buf.length) { - PDEBUG(D_ERR|D_FRAM, "bad frame size %d - %d", - cnt, frame->v4l2_buf.length); - cnt = -EINVAL; - goto out; - } - copy_from_user(frame->data, buf, cnt); - frame->v4l2_buf.bytesused = cnt; - frame->v4l2_buf.flags &= ~(V4L2_BUF_FLAG_QUEUED - | GSPCA_BUF_FLAG_DECODE); - frame->v4l2_buf.flags |= V4L2_BUF_FLAG_DONE; - mutex_unlock(&gspca_dev->queue_lock); - atomic_inc(&gspca_dev->nevent); - wake_up_interruptible(&gspca_dev->wq); /* event = new frame */ - PDEBUG(D_FRAM, "hlp write q:%d i:%d d:%d o:%d", - gspca_dev->fr_q, - gspca_dev->fr_i, - hlp_dev->fr_d, - gspca_dev->fr_o); - return cnt; -out: - mutex_unlock(&gspca_dev->queue_lock); - return cnt; -} - -static struct file_operations hlp_fops = { - .owner = THIS_MODULE, - .open = hlp_open, - .release = hlp_close, - .read = hlp_read, - .write = hlp_write, - .llseek = no_llseek -}; -static struct miscdevice hlp_device = { - .minor = MISC_DYNAMIC_MINOR, - .name = "gspca_hlp", - .fops = &hlp_fops, -}; -#endif /* * VMA operations. @@ -331,10 +114,14 @@ static void fill_frame(struct gspca_dev *gspca_dev, struct urb *urb) { struct gspca_frame *frame; - unsigned char *data; /* address of data in the iso message */ + __u8 *data; /* address of data in the iso message */ int i, j, len, st; cam_pkt_op pkt_scan; + if (urb->status != 0) { + PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); + return; /* disconnection ? */ + } pkt_scan = gspca_dev->sd_desc->pkt_scan; for (i = 0; i < urb->number_of_packets; i++) { @@ -350,20 +137,21 @@ static void fill_frame(struct gspca_dev *gspca_dev, /* check the packet status and length */ len = urb->iso_frame_desc[i].actual_length; + if (len == 0) + continue; st = urb->iso_frame_desc[i].status; if (st) { - PDEBUG(D_ERR, "ISOC data error: [%d] len=%d, status=%d", + PDEBUG(D_ERR, + "ISOC data error: [%d] len=%d, status=%d", i, len, st); gspca_dev->last_packet_type = DISCARD_PACKET; continue; } - if (len == 0) - continue; /* let the packet be analyzed by the subdriver */ PDEBUG(D_PACK, "packet [%d] o:%d l:%d", i, urb->iso_frame_desc[i].offset, len); - data = (unsigned char *) urb->transfer_buffer + data = (__u8 *) urb->transfer_buffer + urb->iso_frame_desc[i].offset; pkt_scan(gspca_dev, frame, data, len); } @@ -390,7 +178,8 @@ static void fill_frame(struct gspca_dev *gspca_dev, * buffers are in user space (userptr). The frame detection * and copy is done by the application. */ -static void isoc_irq_mmap(struct urb *urb) +static void isoc_irq_mmap(struct urb *urb +) { struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; @@ -400,7 +189,8 @@ static void isoc_irq_mmap(struct urb *urb) fill_frame(gspca_dev, urb); } -static void isoc_irq_user(struct urb *urb) +static void isoc_irq_user(struct urb *urb +) { struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; int i; @@ -459,7 +249,7 @@ static void isoc_transfer(struct gspca_dev *gspca_dev) struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, int packet_type, struct gspca_frame *frame, - unsigned char *data, + __u8 *data, int len) { int i, j; @@ -503,23 +293,10 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, /* if last packet, wake the application and advance in the queue */ if (packet_type == LAST_PACKET) { frame->v4l2_buf.bytesused = frame->data_end - frame->data; -#ifndef GSPCA_HLP frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_QUEUED; frame->v4l2_buf.flags |= V4L2_BUF_FLAG_DONE; atomic_inc(&gspca_dev->nevent); wake_up_interruptible(&gspca_dev->wq); /* event = new frame */ -#else /*GSPCA_HLP*/ - if (hlp != 0 && hlp->gspca_dev == gspca_dev) { - frame->v4l2_buf.flags |= GSPCA_BUF_FLAG_DECODE; - atomic_inc(&hlp->nevent); - wake_up_interruptible(&hlp->wq); - } else { - frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_QUEUED; - frame->v4l2_buf.flags |= V4L2_BUF_FLAG_DONE; - atomic_inc(&gspca_dev->nevent); - wake_up_interruptible(&gspca_dev->wq); /* new frame */ - } -#endif /*GSPCA_HLP*/ i = (gspca_dev->fr_i + 1) % gspca_dev->nframes; gspca_dev->fr_i = i; PDEBUG(D_FRAM, "frame complete len:%d q:%d i:%d o:%d", @@ -581,13 +358,13 @@ static void rvfree(void *mem, unsigned long size) static __u32 get_v4l2_depth(__u32 pixfmt) { switch (pixfmt) { - case V4L2_PIX_FMT_BGR32: +/* case V4L2_PIX_FMT_BGR32: case V4L2_PIX_FMT_RGB32: - return 32; + return 32; */ case V4L2_PIX_FMT_RGB24: /* 'RGB3' */ case V4L2_PIX_FMT_BGR24: return 24; - case V4L2_PIX_FMT_RGB565: /* 'RGBP' */ +/* case V4L2_PIX_FMT_RGB565: * 'RGBP' */ case V4L2_PIX_FMT_YUYV: /* 'YUYV' packed 4.2.2 */ case V4L2_PIX_FMT_YYUV: /* 'YYUV' */ return 16; @@ -596,6 +373,9 @@ static __u32 get_v4l2_depth(__u32 pixfmt) case V4L2_PIX_FMT_MJPEG: case V4L2_PIX_FMT_JPEG: case V4L2_PIX_FMT_SBGGR8: /* 'BA81' Bayer */ + case V4L2_PIX_FMT_SN9C10X: /* 'S910' SN9C10x compression */ + case V4L2_PIX_FMT_SPCA501: /* 'S501' YUYV per line */ + case V4L2_PIX_FMT_SPCA561: /* 'S561' compressed BGGR bayer */ return 8; } PDEBUG(D_ERR|D_CONF, "Unknown pixel format %c%c%c%c", @@ -603,7 +383,7 @@ static __u32 get_v4l2_depth(__u32 pixfmt) (pixfmt >> 8) & 0xff, (pixfmt >> 16) & 0xff, pixfmt >> 24); - return -EINVAL; + return 24; } static int gspca_get_buff_size(struct gspca_dev *gspca_dev) @@ -632,7 +412,7 @@ static int frame_alloc(struct gspca_dev *gspca_dev, count = GSPCA_MAX_FRAMES; /* if compressed (JPEG), reduce the buffer size */ if (gspca_is_compressed(gspca_dev->pixfmt)) - frsz = (frsz * comp_fac) / 100 + 600; /* plus JPEG header */ + frsz = (frsz * comp_fac) / 100 + 600; /* (+ JPEG header sz) */ frsz = PAGE_ALIGN(frsz); PDEBUG(D_STREAM, "new fr_sz: %d", frsz); gspca_dev->frsz = frsz; @@ -660,17 +440,6 @@ static int frame_alloc(struct gspca_dev *gspca_dev, } } gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0; -#ifdef GSPCA_HLP - { - struct hlp_dev *hlp_dev; - - hlp_dev = hlp; - if (hlp != 0 && hlp_dev->gspca_dev == gspca_dev) { - hlp_dev->fr_d = 0; - atomic_set(&hlp_dev->nevent, 0); - } - } -#endif /*GSPCA_HLP*/ gspca_dev->last_packet_type = DISCARD_PACKET; gspca_dev->sequence = 0; atomic_set(&gspca_dev->nevent, 0); @@ -752,13 +521,14 @@ struct usb_host_endpoint *get_isoc_ep(struct gspca_dev *gspca_dev) int i, ret; intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); + ep = NULL; i = gspca_dev->alt; /* previous alt setting */ while (--i > 0) { /* alt 0 is unusable */ ep = alt_isoc(&intf->altsetting[i], gspca_dev->cam.epaddr); if (ep) break; } - if (i <= 0) { + if (ep == NULL) { err("no ISOC endpoint found"); return NULL; } @@ -796,11 +566,14 @@ static int create_urbs(struct gspca_dev *gspca_dev, "isoc %d pkts size %d (bsize:%d)", npkt, psize, bsize); /*fixme:change for userptr*/ /*fixme:don't submit all URBs when userptr*/ - gspca_dev->nurbs = nurbs = DEF_NURBS; - if (gspca_dev->memory == V4L2_MEMORY_MMAP) + if (gspca_dev->memory == V4L2_MEMORY_MMAP) { usb_complete = isoc_irq_mmap; - else + nurbs = DEF_NURBS; + } else { usb_complete = isoc_irq_user; + nurbs = USR_NURBS; + } + gspca_dev->nurbs = nurbs; for (n = 0; n < nurbs; n++) { urb = usb_alloc_urb(npkt, GFP_KERNEL); if (!urb) { @@ -904,16 +677,6 @@ static void gspca_stream_off(struct gspca_dev *gspca_dev) { gspca_dev->streaming = 0; atomic_set(&gspca_dev->nevent, 0); -#ifdef GSPCA_HLP - { - struct hlp_dev *hlp_dev; - - hlp_dev = hlp; - if (hlp_dev != 0 - && hlp_dev->gspca_dev == gspca_dev) - atomic_set(&hlp_dev->nevent, 0); - } -#endif if (gspca_dev->present) { gspca_dev->sd_desc->stopN(gspca_dev); destroy_urbs(gspca_dev); @@ -979,15 +742,11 @@ static int vidioc_enum_fmt_cap(struct file *file, void *priv, struct v4l2_fmtdesc *fmtdesc) { struct gspca_dev *gspca_dev = priv; - int i; -#ifndef GSPCA_HLP - int j, index; + int i, j, index; __u32 fmt_tb[8]; -#endif PDEBUG(D_CONF, "enum fmt cap"); -#ifndef GSPCA_HLP /* give an index to each format */ index = 0; j = 0; @@ -1013,36 +772,6 @@ static int vidioc_enum_fmt_cap(struct file *file, void *priv, fmtdesc->pixelformat = fmt_tb[index]; if (gspca_is_compressed(fmt_tb[index])) fmtdesc->flags = V4L2_FMT_FLAG_COMPRESSED; -#else /*GSPCA_HLP*/ - /* !! code tied to the decoding functions in decoder.c */ - i = gspca_dev->cam.nmodes - 1; - if (fmtdesc->index == 0) { /* (assume one format per subdriver) */ - fmtdesc->pixelformat = gspca_dev->cam.cam_mode[i].pixfmt; - if (gspca_is_compressed(fmtdesc->pixelformat)) - fmtdesc->flags = V4L2_FMT_FLAG_COMPRESSED; - } else { - if (hlp == 0 - || (hlp->gspca_dev != 0 - && hlp->gspca_dev != gspca_dev)) - return -EINVAL; - switch (gspca_dev->cam.cam_mode[i].pixfmt) { - case V4L2_PIX_FMT_JPEG: - if (fmtdesc->index >= sizeof jpeg_to_tb - / sizeof jpeg_to_tb[0]) - return -EINVAL; - fmtdesc->pixelformat = jpeg_to_tb[fmtdesc->index]; - break; - case V4L2_PIX_FMT_SBGGR8: - if (fmtdesc->index >= sizeof bayer_to_tb - / sizeof bayer_to_tb[0]) - return -EINVAL; - fmtdesc->pixelformat = bayer_to_tb[fmtdesc->index]; - break; - default: - return -EINVAL; - } - } -#endif /*GSPCA_HLP*/ fmtdesc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmtdesc->description[0] = fmtdesc->pixelformat & 0xff; fmtdesc->description[1] = (fmtdesc->pixelformat >> 8) & 0xff; @@ -1057,25 +786,12 @@ static int vidioc_g_fmt_cap(struct file *file, void *priv, { struct gspca_dev *gspca_dev = priv; -#ifdef GSPCA_HLP - int i; - - /* if the pixel format is not the one of the device and - * if the helper is inactive or busy, restore */ - i = gspca_dev->curr_mode; - if (gspca_dev->pixfmt != gspca_dev->cam.cam_mode[i].pixfmt) { - struct hlp_dev *hlp_dev; - - hlp_dev = hlp; - if (hlp_dev == 0 || hlp_dev->gspca_dev != gspca_dev) - gspca_dev->pixfmt = gspca_dev->cam.cam_mode[i].pixfmt; - } -#endif /*GSPCA_HLP*/ - + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; fmt->fmt.pix.width = gspca_dev->width; fmt->fmt.pix.height = gspca_dev->height; fmt->fmt.pix.pixelformat = gspca_dev->pixfmt; -#ifdef GSPCA_DEBUG +#ifdef VIDEO_ADV_DEBUG if (gspca_debug & D_CONF) { PDEBUG_MODE("get fmt cap", fmt->fmt.pix.pixelformat, @@ -1099,13 +815,15 @@ static int try_fmt_cap(struct gspca_dev *gspca_dev, { int w, h, mode, mode2, frsz; + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; w = fmt->fmt.pix.width; h = fmt->fmt.pix.height; /* (luvcview problem) */ if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG; -#ifdef GSPCA_DEBUG +#ifdef VIDEO_ADV_DEBUG if (gspca_debug & D_CONF) PDEBUG_MODE("try fmt cap", fmt->fmt.pix.pixelformat, w, h); #endif @@ -1121,44 +839,11 @@ static int try_fmt_cap(struct gspca_dev *gspca_dev, if (mode2 >= 0) { mode = mode2; } else { - __u32 pixfmt; - - pixfmt = gspca_dev->cam.cam_mode[mode].pixfmt; -#ifndef GSPCA_HLP /* no chance, return this mode */ - fmt->fmt.pix.pixelformat = pixfmt; -#else /*GSPCA_HLP*/ - if (hlp != 0 - && (hlp->gspca_dev == 0 - || hlp->gspca_dev == gspca_dev) -/* decoding works for JPEG and Bayer only */ - && (pixfmt == V4L2_PIX_FMT_JPEG - || pixfmt == V4L2_PIX_FMT_SBGGR8)) { - switch (fmt->fmt.pix.pixelformat) { - case V4L2_PIX_FMT_YUYV: /* 'YUYV' */ - case V4L2_PIX_FMT_BGR24: /* 'BGR3' */ - case V4L2_PIX_FMT_RGB24: /* 'RGB3' */ - case V4L2_PIX_FMT_YUV420: /* 'YU12' */ - case V4L2_PIX_FMT_RGB565: /* 'RGBP' */ - break; - default: { - /* return any of the supported fmt's */ - __u8 u; - - u = get_jiffies_64(); - u %= sizeof bayer_to_tb - / sizeof bayer_to_tb[0] - 1; - fmt->fmt.pix.pixelformat = - bayer_to_tb[u + 1]; - break; - } - } - } else { - fmt->fmt.pix.pixelformat = pixfmt; - } -#endif /*GSPCA_HLP*/ -#ifdef GSPCA_DEBUG + fmt->fmt.pix.pixelformat = + gspca_dev->cam.cam_mode[mode].pixfmt; +#ifdef VIDEO_ADV_DEBUG if (gspca_debug & D_CONF) { PDEBUG_MODE("new format", fmt->fmt.pix.pixelformat, @@ -1198,7 +883,17 @@ static int vidioc_s_fmt_cap(struct file *file, void *priv, struct gspca_dev *gspca_dev = priv; int ret; -#ifdef GSPCA_DEBUG +#ifdef CONFIG_VIDEO_V4L1_COMPAT + /* if v4l1 got JPEG */ + if (fmt->fmt.pix.pixelformat == 0 + && gspca_dev->streaming) { + fmt->fmt.pix.width = gspca_dev->width; + fmt->fmt.pix.height = gspca_dev->height; + fmt->fmt.pix.pixelformat = gspca_dev->pixfmt; + return 0; + } +#endif +#ifdef VIDEO_ADV_DEBUG if (gspca_debug & D_CONF) { PDEBUG_MODE("set fmt cap", fmt->fmt.pix.pixelformat, @@ -1218,14 +913,8 @@ static int vidioc_s_fmt_cap(struct file *file, void *priv, goto out; } -#ifndef GSPCA_HLP if (ret == gspca_dev->curr_mode) goto out; /* same mode */ -#else /*GSPCA_HLP*/ - if (ret == gspca_dev->curr_mode - && gspca_dev->pixfmt == fmt->fmt.pix.pixelformat) - goto out; /* same mode */ -#endif /*GSPCA_HLP*/ if (gspca_dev->streaming) { ret = -EBUSY; @@ -1236,26 +925,6 @@ static int vidioc_s_fmt_cap(struct file *file, void *priv, gspca_dev->pixfmt = fmt->fmt.pix.pixelformat; gspca_dev->curr_mode = ret; -#ifdef GSPCA_HLP - /* if frame decoding is required */ - if (gspca_dev->pixfmt != gspca_dev->cam.cam_mode[ret].pixfmt) { - struct hlp_dev *hlp_dev; - - hlp_dev = hlp; - if (hlp_dev == 0 - || (hlp_dev->gspca_dev != 0 - && hlp_dev->gspca_dev != gspca_dev)) { /* helper busy */ - fmt->fmt.pix.pixelformat = - gspca_dev->pixfmt = - gspca_dev->cam.cam_mode[ret].pixfmt; - } else { /* helper active */ - hlp_dev->gspca_dev = gspca_dev; - hlp_dev->pixfmt = gspca_dev->cam.cam_mode[ret].pixfmt; - hlp_dev->fr_d = gspca_dev->fr_i; - } - } else if (hlp != 0 && hlp->gspca_dev == gspca_dev) - hlp->gspca_dev = 0; -#endif /*GSPCA_HLP*/ ret = 0; out: mutex_unlock(&gspca_dev->queue_lock); @@ -1294,7 +963,7 @@ static int dev_open(struct inode *inode, struct file *file) } gspca_dev->users++; file->private_data = gspca_dev; -#ifdef GSPCA_DEBUG +#ifdef VIDEO_ADV_DEBUG /* activate the v4l2 debug */ if (gspca_debug & D_V4L2) gspca_dev->vdev.debug |= 3; @@ -1329,22 +998,6 @@ static int dev_close(struct inode *inode, struct file *file) frame_free(gspca_dev); gspca_dev->capt_file = 0; gspca_dev->memory = GSPCA_MEMORY_NO; -#ifdef GSPCA_HLP - { - struct hlp_dev *hlp_dev; - int mode; - - hlp_dev = hlp; - if (hlp_dev != 0 - && hlp_dev->gspca_dev == gspca_dev) { - hlp_dev->gspca_dev = 0; - hlp_dev->frame = 0; - mode = gspca_dev->curr_mode; - gspca_dev->pixfmt = - gspca_dev->cam.cam_mode[mode].pixfmt; - } - } -#endif /*GSPCA_HLP*/ } file->private_data = NULL; mutex_unlock(&gspca_dev->queue_lock); @@ -1370,23 +1023,38 @@ static int vidioc_querycap(struct file *file, void *priv, return 0; } +/* the use of V4L2_CTRL_FLAG_NEXT_CTRL asks for the controls to be sorted */ static int vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *q_ctrl) { struct gspca_dev *gspca_dev = priv; int i; - - PDEBUG(D_CONF, "queryctrl"); + u32 id; + + id = q_ctrl->id; + if (id & V4L2_CTRL_FLAG_NEXT_CTRL) { + id &= V4L2_CTRL_ID_MASK; + id++; + for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) { + if (id >= gspca_dev->sd_desc->ctrls[i].qctrl.id) { + memcpy(q_ctrl, + &gspca_dev->sd_desc->ctrls[i].qctrl, + sizeof *q_ctrl); + return 0; + } + } + return -EINVAL; + } for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) { - if (q_ctrl->id == gspca_dev->sd_desc->ctrls[i].qctrl.id) { + if (id == gspca_dev->sd_desc->ctrls[i].qctrl.id) { memcpy(q_ctrl, &gspca_dev->sd_desc->ctrls[i].qctrl, sizeof *q_ctrl); return 0; } } - if (q_ctrl->id >= V4L2_CID_BASE - && q_ctrl->id <= V4L2_CID_LASTP1) { + if (id >= V4L2_CID_BASE + && id <= V4L2_CID_LASTP1) { q_ctrl->flags |= V4L2_CTRL_FLAG_DISABLED; return 0; } @@ -1489,13 +1157,8 @@ static int vidioc_reqbufs(struct file *file, void *priv, return -EINVAL; switch (rb->memory) { case V4L2_MEMORY_MMAP: - break; case V4L2_MEMORY_USERPTR: -#ifdef GSPCA_HLP - if (hlp == 0 || hlp->gspca_dev != gspca_dev) - break; -#endif - return -EINVAL; + break; default: return -EINVAL; } @@ -1578,7 +1241,7 @@ static int vidioc_streamon(struct file *file, void *priv, if (ret < 0) goto out; } -#ifdef GSPCA_DEBUG +#ifdef VIDEO_ADV_DEBUG if (gspca_debug & D_STREAM) { PDEBUG_MODE("stream on OK", gspca_dev->pixfmt, @@ -1657,7 +1320,7 @@ static int vidioc_g_parm(struct file *filp, void *priv, { struct gspca_dev *gspca_dev = priv; - memset(parm, 0, sizeof parm); + memset(parm, 0, sizeof *parm); parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; parm->parm.capture.readbuffers = gspca_dev->nbufread; return 0; @@ -1677,6 +1340,12 @@ static int vidioc_s_parm(struct file *filp, void *priv, return 0; } +static int vidioc_s_std(struct file *filp, void *priv, + v4l2_std_id *parm) +{ + return 0; +} + #ifdef CONFIG_VIDEO_V4L1_COMPAT static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf) @@ -1686,29 +1355,32 @@ static int vidiocgmbuf(struct file *file, void *priv, PDEBUG(D_STREAM, "cgmbuf"); if (gspca_dev->nframes == 0) { - struct v4l2_requestbuffers rb; int ret; - __u32 pixfmt; - short width, height; - - /* as the final format is not yet defined, allocate - buffers with the max size */ - pixfmt = gspca_dev->pixfmt; - width = gspca_dev->width; - height = gspca_dev->height; - gspca_dev->pixfmt = V4L2_PIX_FMT_BGR32; - gspca_dev->width = 640; - gspca_dev->height = 480; - memset(&rb, 0, sizeof rb); - rb.count = 4; - rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - rb.memory = V4L2_MEMORY_MMAP; - ret = vidioc_reqbufs(file, priv, &rb); - gspca_dev->pixfmt = pixfmt; - gspca_dev->width = width; - gspca_dev->height = height; - if (ret != 0) - return ret; + + { + struct v4l2_format fmt; + + memset(&fmt, 0, sizeof fmt); + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + i = gspca_dev->cam.nmodes - 1; /* highest mode */ + fmt.fmt.pix.width = gspca_dev->cam.cam_mode[i].width; + fmt.fmt.pix.height = gspca_dev->cam.cam_mode[i].height; + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_BGR24; + ret = vidioc_s_fmt_cap(file, priv, &fmt); + if (ret != 0) + return ret; + } + { + struct v4l2_requestbuffers rb; + + memset(&rb, 0, sizeof rb); + rb.count = 4; + rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + rb.memory = V4L2_MEMORY_MMAP; + ret = vidioc_reqbufs(file, priv, &rb); + if (ret != 0) + return ret; + } } mbuf->frames = gspca_dev->nframes; mbuf->size = gspca_dev->frsz * gspca_dev->nframes; @@ -1951,7 +1623,7 @@ static int vidioc_qbuf(struct file *file, void *priv, if (frame->v4l2_buf.memory == V4L2_MEMORY_USERPTR) { frame->data = frame->data_end = - (unsigned char *) v4l2_buf->m.userptr; + (__u8 *) v4l2_buf->m.userptr; frame->v4l2_buf.m.userptr = v4l2_buf->m.userptr; frame->v4l2_buf.length = v4l2_buf->length; } @@ -2154,6 +1826,9 @@ static struct file_operations dev_fops = { .read = dev_read, .mmap = dev_mmap, .ioctl = video_ioctl2, +#ifdef CONFIG_COMPAT + .compat_ioctl = v4l_compat_ioctl32, +#endif .llseek = no_llseek, .poll = dev_poll, }; @@ -2186,6 +1861,7 @@ static struct video_device gspca_template = { .vidioc_s_jpegcomp = vidioc_s_jpegcomp, .vidioc_g_parm = vidioc_g_parm, .vidioc_s_parm = vidioc_s_parm, + .vidioc_s_std = vidioc_s_std, #ifdef CONFIG_VIDEO_V4L1_COMPAT .vidiocgmbuf = vidiocgmbuf, #endif @@ -2207,12 +1883,8 @@ int gspca_dev_probe(struct usb_interface *intf, struct gspca_dev *gspca_dev; struct usb_device *dev = interface_to_usbdev(intf); int ret; - __u16 vendor; - __u16 product; - vendor = id->idVendor; - product = id->idProduct; - PDEBUG(D_PROBE, "probing %04x:%04x", vendor, product); + PDEBUG(D_PROBE, "probing %04x:%04x", id->idVendor, id->idProduct); /* we don't handle multi-config cameras */ if (dev->descriptor.bNumConfigurations != 1) @@ -2309,35 +1981,24 @@ EXPORT_SYMBOL(gspca_disconnect); /* -- module insert / remove -- */ static int __init gspca_init(void) { -#ifdef GSPCA_HLP - int ret; - - /* create /dev/gspca_hlp */ - ret = misc_register(&hlp_device); - if (ret < 0) - err("misc_register err %d", ret); - start_hlp(); /* try to start the helper process */ -#endif info("main v%s registered", version); return 0; } static void __exit gspca_exit(void) { -#ifdef GSPCA_HLP - misc_deregister(&hlp_device); -#endif info("main deregistered"); } module_init(gspca_init); module_exit(gspca_exit); +#ifdef VIDEO_ADV_DEBUG module_param_named(debug, gspca_debug, int, 0644); MODULE_PARM_DESC(debug, "Debug (bit) 0x01:error 0x02:probe 0x04:config" " 0x08:stream 0x10:frame 0x20:packet 0x40:USBin 0x80:USBout" " 0x0100: v4l2"); - +#endif module_param(comp_fac, int, 0644); MODULE_PARM_DESC(comp_fac, "Buffer size ratio when compressed in percent"); diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index e69d8472a284..1581fa808b6f 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -9,7 +9,26 @@ #include #include -#ifdef GSPCA_DEBUG +/* values in 2.6.27 */ +#ifndef V4L2_PIX_FMT_SPCA501 +#define V4L2_PIX_FMT_SPCA501 v4l2_fourcc('S', '5', '0', '1') +#endif +#ifndef V4L2_PIX_FMT_SPCA561 +#define V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S', '5', '6', '1') +#endif + +/* values in 2.6.26 */ +#ifndef V4L2_CID_POWER_LINE_FREQUENCY +#define V4L2_CID_POWER_LINE_FREQUENCY (V4L2_CID_BASE+24) +#endif +#ifndef V4L2_CID_WHITE_BALANCE_TEMPERATURE +#define V4L2_CID_WHITE_BALANCE_TEMPERATURE (V4L2_CID_BASE + 26) +#endif +#ifndef V4L2_CID_SHARPNESS +#define V4L2_CID_SHARPNESS (V4L2_CID_BASE+27) +#endif + +#ifdef VIDEO_ADV_DEBUG /* GSPCA our debug messages */ extern int gspca_debug; #define PDEBUG(level, fmt, args...) \ @@ -47,7 +66,7 @@ extern int gspca_debug; #define GSPCA_MAX_FRAMES 16 /* maximum number of video frame buffers */ /* ISOC transfers */ -#define MAX_NURBS 32 /* max number of URBs (read & userptr) */ +#define MAX_NURBS 16 /* max number of URBs */ #define ISO_MAX_PKT 32 /* max number of packets in an ISOC transfer */ #define ISO_MAX_SIZE 0x8000 /* max size of one URB buffer (32 Kb) */ @@ -79,7 +98,7 @@ typedef int (*cam_qmnu_op) (struct gspca_dev *, struct v4l2_querymenu *); typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev, struct gspca_frame *frame, - unsigned char *data, + __u8 *data, int len); struct ctrl { @@ -116,8 +135,8 @@ struct sd_desc { #define LAST_PACKET 3 struct gspca_frame { - unsigned char *data; /* frame buffer */ - unsigned char *data_end; /* end of frame while filling */ + __u8 *data; /* frame buffer */ + __u8 *data_end; /* end of frame while filling */ int vma_use_count; struct v4l2_buffer v4l2_buf; }; @@ -135,7 +154,7 @@ struct gspca_dev { __u8 *frbuf; /* buffer for nframes */ struct gspca_frame frame[GSPCA_MAX_FRAMES]; - unsigned int frsz; /* frame size */ + __u32 frsz; /* frame size */ char nframes; /* number of frames */ char fr_i; /* frame being filled */ char fr_q; /* next frame to queue */ @@ -145,10 +164,10 @@ struct gspca_dev { __u8 iface; /* USB interface number */ __u8 alt; /* USB alternate setting */ - unsigned char curr_mode; /* current camera mode */ + __u8 curr_mode; /* current camera mode */ __u32 pixfmt; /* current mode parameters */ - short width; - short height; + __u16 width; + __u16 height; atomic_t nevent; /* number of frames done */ wait_queue_head_t wq; /* wait queue */ @@ -176,6 +195,6 @@ void gspca_disconnect(struct usb_interface *intf); struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, int packet_type, struct gspca_frame *frame, - unsigned char *data, + __u8 *data, int len); #endif /* GSPCAV2_H */ diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c new file mode 100644 index 000000000000..48b861d68299 --- /dev/null +++ b/drivers/media/video/gspca/mars.c @@ -0,0 +1,455 @@ +/* + * Mars-Semi MR97311A library + * Copyright (C) 2005 + * + * V4L2 by Jean-Francois Moine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "mars" + +#include "gspca.h" +#include "jpeg.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/Mars USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + char qindex; +}; + +/* V4L2 controls supported by the driver */ +static struct ctrl sd_ctrls[] = { +}; + +static struct cam_mode vga_mode[] = { + {V4L2_PIX_FMT_JPEG, 320, 240, 2}, + {V4L2_PIX_FMT_JPEG, 640, 480, 1}, +}; + +/* MI Register table //elvis */ +enum { + REG_HW_MI_0, + REG_HW_MI_1, + REG_HW_MI_2, + REG_HW_MI_3, + REG_HW_MI_4, + REG_HW_MI_5, + REG_HW_MI_6, + REG_HW_MI_7, + REG_HW_MI_9 = 0x09, + REG_HW_MI_B = 0x0B, + REG_HW_MI_C, + REG_HW_MI_D, + REG_HW_MI_1E = 0x1E, + REG_HW_MI_20 = 0x20, + REG_HW_MI_2B = 0x2B, + REG_HW_MI_2C, + REG_HW_MI_2D, + REG_HW_MI_2E, + REG_HW_MI_35 = 0x35, + REG_HW_MI_5F = 0x5f, + REG_HW_MI_60, + REG_HW_MI_61, + REG_HW_MI_62, + REG_HW_MI_63, + REG_HW_MI_64, + REG_HW_MI_F1 = 0xf1, + ATTR_TOTAL_MI_REG = 242 +}; + +static int pcam_reg_write(struct usb_device *dev, + __u16 index, unsigned char *value, int length) +{ + int rc; + + rc = usb_control_msg(dev, + usb_sndbulkpipe(dev, 4), + 0x12, +/* ?? 0xc8 = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_??? !? */ + 0xc8, + 0, /* value */ + index, value, length, 500); + PDEBUG(D_USBO, "reg write: 0x%02X , result = 0x%x", index, rc); + + if (rc < 0) + PDEBUG(D_ERR, "reg write: error %d", rc); + return rc; +} + +static void MISensor_BulkWrite(struct usb_device *dev, unsigned short *pch, + char Address) +{ + int result; + unsigned char data[6]; + + data[0] = 0x1f; + data[1] = 0; + data[2] = Address; + data[3] = *pch >> 8; /* high byte */ + data[4] = *pch; /* low byte */ + data[5] = 0; + + result = usb_control_msg(dev, + usb_sndbulkpipe(dev, 4), + 0x12, +/* ?? 0xc8 = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_??? !? */ + 0xc8, + 0, /* value */ + Address, /* index */ + data, 5, 500); + PDEBUG(D_USBO, "bulk write 0x%02x = 0x%04x", Address, *pch); + + if (result < 0) + PDEBUG(D_ERR, "reg write: error %d", result); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + cam->cam_mode = vga_mode; + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + sd->qindex = 1; /* set the quantization table */ + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + int err_code; + __u8 data[12]; + __u16 MI_buf[242]; + int h_size, v_size; + int intpipe; + /* struct usb_device *dev = pcam->dev; */ + memset(data, 0, sizeof data); + memset(MI_buf, 0, sizeof MI_buf); + + PDEBUG(D_STREAM, "camera start, iface %d, alt 8", gspca_dev->iface); + if (usb_set_interface(dev, gspca_dev->iface, 8) < 0) { + PDEBUG(D_ERR|D_STREAM, "Set packet size: set interface error"); + return; + } + + data[0] = 0x01; /* address */ + data[1] = 0x01; + + err_code = pcam_reg_write(dev, data[0], data, 0x02); + if (err_code < 0) + return; + + /* + Initialize the MR97113 chip register + */ + data[0] = 0x00; /* address */ + data[1] = 0x0c | 0x01; /* reg 0 */ + data[2] = 0x01; /* reg 1 */ + h_size = gspca_dev->width; + v_size = gspca_dev->height; + data[3] = h_size / 8; /* h_size , reg 2 */ + data[4] = v_size / 8; /* v_size , reg 3 */ + data[5] = 0x30; /* reg 4, MI, PAS5101 : + * 0x30 for 24mhz , 0x28 for 12mhz */ + data[6] = 4; /* reg 5, H start */ + data[7] = 0xc0; /* reg 6, gamma 1.5 */ + data[8] = 3; /* reg 7, V start */ +/* if(h_size == 320 ) */ +/* data[9]= 0x56; * reg 8, 24MHz, 2:1 scale down */ +/* else */ + data[9] = 0x52; /* reg 8, 24MHz, no scale down */ + data[10] = 0x5d; /* reg 9, I2C device address + * [for PAS5101 (0x40)] [for MI (0x5d)] */ + + err_code = pcam_reg_write(dev, data[0], data, 0x0b); + if (err_code < 0) + return; + + data[0] = 0x23; /* address */ + data[1] = 0x09; /* reg 35, append frame header */ + + err_code = pcam_reg_write(dev, data[0], data, 0x02); + if (err_code < 0) { + PDEBUG(D_ERR, "Register write failed"); + return; + } + + data[0] = 0x3C; /* address */ +/* if (pcam->width == 1280) */ +/* data[1] = 200; * reg 60, pc-cam frame size + * (unit: 4KB) 800KB */ +/* else */ + data[1] = 50; /* 50 reg 60, pc-cam frame size + * (unit: 4KB) 200KB */ + err_code = pcam_reg_write(dev, data[0], data, 0x02); + if (err_code < 0) + return; + + if (0) { /* fixed dark-gain */ + data[1] = 0; /* reg 94, Y Gain (1.75) */ + data[2] = 0; /* reg 95, UV Gain (1.75) */ + data[3] = 0x3f; /* reg 96, Y Gain/UV Gain/disable auto dark-gain */ + data[4] = 0; /* reg 97, set fixed dark level */ + data[5] = 0; /* reg 98, don't care */ + } else { /* auto dark-gain */ + data[1] = 0; /* reg 94, Y Gain (auto) */ + data[2] = 0; /* reg 95, UV Gain (1.75) */ + data[3] = 0x78; /* reg 96, Y Gain/UV Gain/disable auto dark-gain */ + switch (gspca_dev->width) { +/* case 1280: */ +/* data[4] = 154; + * reg 97, %3 shadow point (unit: 256 pixel) */ +/* data[5] = 51; + * reg 98, %1 highlight point + * (uint: 256 pixel) */ +/* break; */ + default: +/* case 640: */ + data[4] = 36; /* reg 97, %3 shadow point + * (unit: 256 pixel) */ + data[5] = 12; /* reg 98, %1 highlight point + * (uint: 256 pixel) */ + break; + case 320: + data[4] = 9; /* reg 97, %3 shadow point + * (unit: 256 pixel) */ + data[5] = 3; /* reg 98, %1 highlight point + * (uint: 256 pixel) */ + break; + } + } + /* auto dark-gain */ + data[0] = 0x5e; /* address */ + + err_code = pcam_reg_write(dev, data[0], data, 0x06); + if (err_code < 0) + return; + + data[0] = 0x67; + data[1] = 0x13; /* reg 103, first pixel B, disable sharpness */ + err_code = pcam_reg_write(dev, data[0], data, 0x02); + if (err_code < 0) + return; + + /* + * initialize the value of MI sensor... + */ + MI_buf[REG_HW_MI_1] = 0x000a; + MI_buf[REG_HW_MI_2] = 0x000c; + MI_buf[REG_HW_MI_3] = 0x0405; + MI_buf[REG_HW_MI_4] = 0x0507; + /* mi_Attr_Reg_[REG_HW_MI_5] = 0x01ff;//13 */ + MI_buf[REG_HW_MI_5] = 0x0013; /* 13 */ + MI_buf[REG_HW_MI_6] = 0x001f; /* vertical blanking */ + /* mi_Attr_Reg_[REG_HW_MI_6] = 0x0400; // vertical blanking */ + MI_buf[REG_HW_MI_7] = 0x0002; + /* mi_Attr_Reg_[REG_HW_MI_9] = 0x015f; */ + /* mi_Attr_Reg_[REG_HW_MI_9] = 0x030f; */ + MI_buf[REG_HW_MI_9] = 0x0374; + MI_buf[REG_HW_MI_B] = 0x0000; + MI_buf[REG_HW_MI_C] = 0x0000; + MI_buf[REG_HW_MI_D] = 0x0000; + MI_buf[REG_HW_MI_1E] = 0x8000; +/* mi_Attr_Reg_[REG_HW_MI_20] = 0x1104; */ + MI_buf[REG_HW_MI_20] = 0x1104; /* 0x111c; */ + MI_buf[REG_HW_MI_2B] = 0x0008; +/* mi_Attr_Reg_[REG_HW_MI_2C] = 0x000f; */ + MI_buf[REG_HW_MI_2C] = 0x001f; /* lita suggest */ + MI_buf[REG_HW_MI_2D] = 0x0008; + MI_buf[REG_HW_MI_2E] = 0x0008; + MI_buf[REG_HW_MI_35] = 0x0051; + MI_buf[REG_HW_MI_5F] = 0x0904; /* fail to write */ + MI_buf[REG_HW_MI_60] = 0x0000; + MI_buf[REG_HW_MI_61] = 0x0000; + MI_buf[REG_HW_MI_62] = 0x0498; + MI_buf[REG_HW_MI_63] = 0x0000; + MI_buf[REG_HW_MI_64] = 0x0000; + MI_buf[REG_HW_MI_F1] = 0x0001; + /* changing while setting up the different value of dx/dy */ + + if (gspca_dev->width != 1280) { + MI_buf[0x01] = 0x010a; + MI_buf[0x02] = 0x014c; + MI_buf[0x03] = 0x01e5; + MI_buf[0x04] = 0x0287; + } + MI_buf[0x20] = 0x1104; + + MISensor_BulkWrite(dev, MI_buf + 1, 1); + MISensor_BulkWrite(dev, MI_buf + 2, 2); + MISensor_BulkWrite(dev, MI_buf + 3, 3); + MISensor_BulkWrite(dev, MI_buf + 4, 4); + MISensor_BulkWrite(dev, MI_buf + 5, 5); + MISensor_BulkWrite(dev, MI_buf + 6, 6); + MISensor_BulkWrite(dev, MI_buf + 7, 7); + MISensor_BulkWrite(dev, MI_buf + 9, 9); + MISensor_BulkWrite(dev, MI_buf + 0x0b, 0x0b); + MISensor_BulkWrite(dev, MI_buf + 0x0c, 0x0c); + MISensor_BulkWrite(dev, MI_buf + 0x0d, 0x0d); + MISensor_BulkWrite(dev, MI_buf + 0x1e, 0x1e); + MISensor_BulkWrite(dev, MI_buf + 0x20, 0x20); + MISensor_BulkWrite(dev, MI_buf + 0x2b, 0x2b); + MISensor_BulkWrite(dev, MI_buf + 0x2c, 0x2c); + MISensor_BulkWrite(dev, MI_buf + 0x2d, 0x2d); + MISensor_BulkWrite(dev, MI_buf + 0x2e, 0x2e); + MISensor_BulkWrite(dev, MI_buf + 0x35, 0x35); + MISensor_BulkWrite(dev, MI_buf + 0x5f, 0x5f); + MISensor_BulkWrite(dev, MI_buf + 0x60, 0x60); + MISensor_BulkWrite(dev, MI_buf + 0x61, 0x61); + MISensor_BulkWrite(dev, MI_buf + 0x62, 0x62); + MISensor_BulkWrite(dev, MI_buf + 0x63, 0x63); + MISensor_BulkWrite(dev, MI_buf + 0x64, 0x64); + MISensor_BulkWrite(dev, MI_buf + 0xf1, 0xf1); + + intpipe = usb_sndintpipe(dev, 0); + err_code = usb_clear_halt(dev, intpipe); + + data[0] = 0x00; + data[1] = 0x4d; /* ISOC transfering enable... */ + pcam_reg_write(dev, data[0], data, 0x02); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + int result; + __u8 data[2]; + + data[0] = 1; + data[1] = 0; + result = pcam_reg_write(gspca_dev->dev, data[0], data, 2); + if (result < 0) + PDEBUG(D_ERR, "Camera Stop failed"); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + unsigned char *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + int p; + + if (len < 6) { +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + for (p = 0; p < len - 6; p++) { + if (data[0 + p] == 0xff + && data[1 + p] == 0xff + && data[2 + p] == 0x00 + && data[3 + p] == 0xff + && data[4 + p] == 0x96) { + if (data[5 + p] == 0x64 + || data[5 + p] == 0x65 + || data[5 + p] == 0x66 + || data[5 + p] == 0x67) { + PDEBUG(D_PACK, "sof offset: %d leng: %d", + p, len); + frame = gspca_frame_add(gspca_dev, LAST_PACKET, + frame, data, 0); + + /* put the JPEG header */ + jpeg_put_header(gspca_dev, frame, + sd->qindex, 0x21); + data += 16; + len -= 16; + break; + } + } + } + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x093a, 0x050f), DVNM("Mars-Semi Pc-Camera")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c new file mode 100644 index 000000000000..7d6237f18ba0 --- /dev/null +++ b/drivers/media/video/gspca/ov519.c @@ -0,0 +1,2174 @@ +/** + * OV519 driver + * + * Copyright (C) 2008 Jean-Francois Moine (http://moinejf.free.fr) + * + * (This module is adapted from the ov51x-jpeg package) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#define MODULE_NAME "ov519" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Jean-Francois Moine "); +MODULE_DESCRIPTION("OV519 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* global parameters */ +static int frame_rate; + +/* Number of times to retry a failed I2C transaction. Increase this if you + * are getting "Failed to read sensor ID..." */ +static int i2c_detect_tries = 10; + +/* ov519 device descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + /* Determined by sensor type */ + short maxwidth; + short maxheight; + + unsigned char primary_i2c_slave; /* I2C write id of sensor */ + + unsigned char brightness; + unsigned char contrast; + unsigned char colors; + + char compress; /* Should the next frame be compressed? */ + char compress_inited; /* Are compression params uploaded? */ + char stopped; /* Streaming is temporarily paused */ + + char frame_rate; /* current Framerate (OV519 only) */ + char clockdiv; /* clockdiv override for OV519 only */ + + char sensor; /* Type of image sensor chip (SEN_*) */ +#define SEN_UNKNOWN 0 +#define SEN_OV6620 1 +#define SEN_OV6630 2 +#define SEN_OV7610 3 +#define SEN_OV7620 4 +#define SEN_OV7630 5 +#define SEN_OV7640 6 +#define SEN_OV7670 7 +#define SEN_OV76BE 8 +#define SEN_OV8610 9 + +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define SD_COLOR 2 + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, +}; + +static struct cam_mode vga_mode[] = { + {V4L2_PIX_FMT_JPEG, 320, 240}, + {V4L2_PIX_FMT_JPEG, 640, 480}, +}; +static struct cam_mode sif_mode[] = { + {V4L2_PIX_FMT_JPEG, 176, 144}, + {V4L2_PIX_FMT_JPEG, 352, 288}, +}; + +/* OV519 Camera interface register numbers */ +#define OV519_CAM_H_SIZE 0x10 +#define OV519_CAM_V_SIZE 0x11 +#define OV519_CAM_X_OFFSETL 0x12 +#define OV519_CAM_X_OFFSETH 0x13 +#define OV519_CAM_Y_OFFSETL 0x14 +#define OV519_CAM_Y_OFFSETH 0x15 +#define OV519_CAM_DIVIDER 0x16 +#define OV519_CAM_DFR 0x20 +#define OV519_CAM_FORMAT 0x25 + +/* OV519 System Controller register numbers */ +#define OV519_SYS_RESET1 0x51 +#define OV519_SYS_EN_CLK1 0x54 + +#define OV519_GPIO_DATA_OUT0 0x71 +#define OV519_GPIO_IO_CTRL0 0x72 + +#define OV511_ENDPOINT_ADDRESS 1 /* Isoc endpoint number */ + +/* I2C registers */ +#define R51x_I2C_W_SID 0x41 +#define R51x_I2C_SADDR_3 0x42 +#define R51x_I2C_SADDR_2 0x43 +#define R51x_I2C_R_SID 0x44 +#define R51x_I2C_DATA 0x45 +#define R518_I2C_CTL 0x47 /* OV518(+) only */ + +/* I2C ADDRESSES */ +#define OV7xx0_SID 0x42 +#define OV8xx0_SID 0xa0 +#define OV6xx0_SID 0xc0 + +/* OV7610 registers */ +#define OV7610_REG_GAIN 0x00 /* gain setting (5:0) */ +#define OV7610_REG_SAT 0x03 /* saturation */ +#define OV8610_REG_HUE 0x04 /* 04 reserved */ +#define OV7610_REG_CNT 0x05 /* Y contrast */ +#define OV7610_REG_BRT 0x06 /* Y brightness */ +#define OV7610_REG_COM_C 0x14 /* misc common regs */ +#define OV7610_REG_ID_HIGH 0x1c /* manufacturer ID MSB */ +#define OV7610_REG_ID_LOW 0x1d /* manufacturer ID LSB */ +#define OV7610_REG_COM_I 0x29 /* misc settings */ + +/* OV7670 registers */ +#define OV7670_REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ +#define OV7670_REG_BLUE 0x01 /* blue gain */ +#define OV7670_REG_RED 0x02 /* red gain */ +#define OV7670_REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ +#define OV7670_REG_COM1 0x04 /* Control 1 */ +#define OV7670_REG_AECHH 0x07 /* AEC MS 5 bits */ +#define OV7670_REG_COM3 0x0c /* Control 3 */ +#define OV7670_REG_COM4 0x0d /* Control 4 */ +#define OV7670_REG_COM5 0x0e /* All "reserved" */ +#define OV7670_REG_COM6 0x0f /* Control 6 */ +#define OV7670_REG_AECH 0x10 /* More bits of AEC value */ +#define OV7670_REG_CLKRC 0x11 /* Clock control */ +#define OV7670_REG_COM7 0x12 /* Control 7 */ +#define OV7670_COM7_FMT_VGA 0x00 +#define OV7670_COM7_YUV 0x00 /* YUV */ +#define OV7670_COM7_FMT_QVGA 0x10 /* QVGA format */ +#define OV7670_COM7_FMT_MASK 0x38 +#define OV7670_COM7_RESET 0x80 /* Register reset */ +#define OV7670_REG_COM8 0x13 /* Control 8 */ +#define OV7670_COM8_AEC 0x01 /* Auto exposure enable */ +#define OV7670_COM8_AWB 0x02 /* White balance enable */ +#define OV7670_COM8_AGC 0x04 /* Auto gain enable */ +#define OV7670_COM8_BFILT 0x20 /* Band filter enable */ +#define OV7670_COM8_AECSTEP 0x40 /* Unlimited AEC step size */ +#define OV7670_COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ +#define OV7670_REG_COM9 0x14 /* Control 9 - gain ceiling */ +#define OV7670_REG_COM10 0x15 /* Control 10 */ +#define OV7670_REG_HSTART 0x17 /* Horiz start high bits */ +#define OV7670_REG_HSTOP 0x18 /* Horiz stop high bits */ +#define OV7670_REG_VSTART 0x19 /* Vert start high bits */ +#define OV7670_REG_VSTOP 0x1a /* Vert stop high bits */ +#define OV7670_REG_MVFP 0x1e /* Mirror / vflip */ +#define OV7670_MVFP_MIRROR 0x20 /* Mirror image */ +#define OV7670_REG_AEW 0x24 /* AGC upper limit */ +#define OV7670_REG_AEB 0x25 /* AGC lower limit */ +#define OV7670_REG_VPT 0x26 /* AGC/AEC fast mode op region */ +#define OV7670_REG_HREF 0x32 /* HREF pieces */ +#define OV7670_REG_TSLB 0x3a /* lots of stuff */ +#define OV7670_REG_COM11 0x3b /* Control 11 */ +#define OV7670_COM11_EXP 0x02 +#define OV7670_COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ +#define OV7670_REG_COM12 0x3c /* Control 12 */ +#define OV7670_REG_COM13 0x3d /* Control 13 */ +#define OV7670_COM13_GAMMA 0x80 /* Gamma enable */ +#define OV7670_COM13_UVSAT 0x40 /* UV saturation auto adjustment */ +#define OV7670_REG_COM14 0x3e /* Control 14 */ +#define OV7670_REG_EDGE 0x3f /* Edge enhancement factor */ +#define OV7670_REG_COM15 0x40 /* Control 15 */ +#define OV7670_COM15_R00FF 0xc0 /* 00 to FF */ +#define OV7670_REG_COM16 0x41 /* Control 16 */ +#define OV7670_COM16_AWBGAIN 0x08 /* AWB gain enable */ +#define OV7670_REG_BRIGHT 0x55 /* Brightness */ +#define OV7670_REG_CONTRAS 0x56 /* Contrast control */ +#define OV7670_REG_GFIX 0x69 /* Fix gain control */ +#define OV7670_REG_RGB444 0x8c /* RGB 444 control */ +#define OV7670_REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */ +#define OV7670_REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */ +#define OV7670_REG_BD50MAX 0xa5 /* 50hz banding step limit */ +#define OV7670_REG_HAECC3 0xa6 /* Hist AEC/AGC control 3 */ +#define OV7670_REG_HAECC4 0xa7 /* Hist AEC/AGC control 4 */ +#define OV7670_REG_HAECC5 0xa8 /* Hist AEC/AGC control 5 */ +#define OV7670_REG_HAECC6 0xa9 /* Hist AEC/AGC control 6 */ +#define OV7670_REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ +#define OV7670_REG_BD60MAX 0xab /* 60hz banding step limit */ + +struct ovsensor_window { + short x; + short y; + short width; + short height; +/* int format; */ + short quarter; /* Scale width and height down 2x */ + short clockdiv; /* Clock divisor setting */ +}; + +static unsigned char ov7670_abs_to_sm(unsigned char v) +{ + if (v > 127) + return v & 0x7f; + return (128 - v) | 0x80; +} + +/* Write a OV519 register */ +static int reg_w(struct sd *sd, __u16 index, __u8 value) +{ + int ret; + __u8 buf[4]; + + buf[0] = value; + ret = usb_control_msg(sd->gspca_dev.dev, + usb_sndctrlpipe(sd->gspca_dev.dev, 0), + 1, /* REQ_IO (ov518/519) */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, + &buf[0], 1, 500); + if (ret < 0) + PDEBUG(D_ERR, "Write reg [%02x] %02x failed", index, value); + return ret; +} + +/* Read from a OV519 register */ +/* returns: negative is error, pos or zero is data */ +static int reg_r(struct sd *sd, __u16 index) +{ + int ret; + __u8 buf[4]; + + ret = usb_control_msg(sd->gspca_dev.dev, + usb_rcvctrlpipe(sd->gspca_dev.dev, 0), + 1, /* REQ_IO */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, &buf[0], 1, 500); + + if (ret >= 0) + ret = buf[0]; + else + PDEBUG(D_ERR, "Read reg [0x%02x] failed", index); + return ret; +} + +/* Read 8 values from a OV519 register */ +static int reg_r8(struct sd *sd, + __u16 index) +{ + int ret; + __u8 buf[8]; + + ret = usb_control_msg(sd->gspca_dev.dev, + usb_rcvctrlpipe(sd->gspca_dev.dev, 0), + 1, /* REQ_IO */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, &buf[0], 8, 500); + + if (ret >= 0) + ret = buf[0]; + else + PDEBUG(D_ERR, "Read reg 8 [0x%02x] failed", index); + return ret; +} + +/* + * Writes bits at positions specified by mask to an OV51x reg. Bits that are in + * the same position as 1's in "mask" are cleared and set to "value". Bits + * that are in the same position as 0's in "mask" are preserved, regardless + * of their respective state in "value". + */ +static int reg_w_mask(struct sd *sd, + __u16 index, + __u8 value, + __u8 mask) +{ + int ret; + __u8 oldval; + + if (mask != 0xff) { + value &= mask; /* Enforce mask on value */ + ret = reg_r(sd, index); + if (ret < 0) + return ret; + + oldval = ret & ~mask; /* Clear the masked bits */ + value |= oldval; /* Set the desired bits */ + } + return reg_w(sd, index, value); +} + +/* + * The OV518 I2C I/O procedure is different, hence, this function. + * This is normally only called from i2c_w(). Note that this function + * always succeeds regardless of whether the sensor is present and working. + */ +static int i2c_w(struct sd *sd, + __u8 reg, + __u8 value) +{ + int rc; + + PDEBUG(D_USBO, "i2c 0x%02x -> [0x%02x]", value, reg); + + /* Select camera register */ + rc = reg_w(sd, R51x_I2C_SADDR_3, reg); + if (rc < 0) + return rc; + + /* Write "value" to I2C data port of OV511 */ + rc = reg_w(sd, R51x_I2C_DATA, value); + if (rc < 0) + return rc; + + /* Initiate 3-byte write cycle */ + rc = reg_w(sd, R518_I2C_CTL, 0x01); + + /* wait for write complete */ + msleep(4); + if (rc < 0) + return rc; + return reg_r8(sd, R518_I2C_CTL); +} + +/* + * returns: negative is error, pos or zero is data + * + * The OV518 I2C I/O procedure is different, hence, this function. + * This is normally only called from i2c_r(). Note that this function + * always succeeds regardless of whether the sensor is present and working. + */ +static int i2c_r(struct sd *sd, __u8 reg) +{ + int rc, value; + + /* Select camera register */ + rc = reg_w(sd, R51x_I2C_SADDR_2, reg); + if (rc < 0) + return rc; + + /* Initiate 2-byte write cycle */ + rc = reg_w(sd, R518_I2C_CTL, 0x03); + if (rc < 0) + return rc; + + /* Initiate 2-byte read cycle */ + rc = reg_w(sd, R518_I2C_CTL, 0x05); + if (rc < 0) + return rc; + value = reg_r(sd, R51x_I2C_DATA); + PDEBUG(D_USBI, "i2c [0x%02X] -> 0x%02X", reg, value); + return value; +} + +/* Writes bits at positions specified by mask to an I2C reg. Bits that are in + * the same position as 1's in "mask" are cleared and set to "value". Bits + * that are in the same position as 0's in "mask" are preserved, regardless + * of their respective state in "value". + */ +static int i2c_w_mask(struct sd *sd, + __u8 reg, + __u8 value, + __u8 mask) +{ + int rc; + __u8 oldval; + + value &= mask; /* Enforce mask on value */ + rc = i2c_r(sd, reg); + if (rc < 0) + return rc; + oldval = rc & ~mask; /* Clear the masked bits */ + value |= oldval; /* Set the desired bits */ + return i2c_w(sd, reg, value); +} + +/* Temporarily stops OV511 from functioning. Must do this before changing + * registers while the camera is streaming */ +static inline int ov51x_stop(struct sd *sd) +{ + PDEBUG(D_STREAM, "stopping"); + sd->stopped = 1; + return reg_w(sd, OV519_SYS_RESET1, 0x0f); +} + +/* Restarts OV511 after ov511_stop() is called. Has no effect if it is not + * actually stopped (for performance). */ +static inline int ov51x_restart(struct sd *sd) +{ + PDEBUG(D_STREAM, "restarting"); + if (!sd->stopped) + return 0; + sd->stopped = 0; + + /* Reinitialize the stream */ + return reg_w(sd, OV519_SYS_RESET1, 0x00); +} + +/* This does an initial reset of an OmniVision sensor and ensures that I2C + * is synchronized. Returns <0 on failure. + */ +static int init_ov_sensor(struct sd *sd) +{ + int i, success; + + /* Reset the sensor */ + if (i2c_w(sd, 0x12, 0x80) < 0) + return -EIO; + + /* Wait for it to initialize */ + msleep(150); + + for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) { + if (i2c_r(sd, OV7610_REG_ID_HIGH) == 0x7f && + i2c_r(sd, OV7610_REG_ID_LOW) == 0xa2) { + success = 1; + continue; + } + + /* Reset the sensor */ + if (i2c_w(sd, 0x12, 0x80) < 0) + return -EIO; + /* Wait for it to initialize */ + msleep(150); + /* Dummy read to sync I2C */ + if (i2c_r(sd, 0x00) < 0) + return -EIO; + } + if (!success) + return -EIO; + PDEBUG(D_PROBE, "I2C synced in %d attempt(s)", i); + return 0; +} + +/* Switch on standard JPEG compression. Returns 0 for success. */ +static int ov519_init_compression(struct sd *sd) +{ + if (!sd->compress_inited) { + if (reg_w_mask(sd, OV519_SYS_EN_CLK1, 1 << 2, 1 << 2) < 0) { + PDEBUG(D_ERR, "Error switching to compressed mode"); + return -EIO; + } + sd->compress_inited = 1; + } + return 0; +} + +/* Set the read and write slave IDs. The "slave" argument is the write slave, + * and the read slave will be set to (slave + 1). + * This should not be called from outside the i2c I/O functions. + * Sets I2C read and write slave IDs. Returns <0 for error + */ +static int ov51x_set_slave_ids(struct sd *sd, + __u8 slave) +{ + int rc; + + rc = reg_w(sd, R51x_I2C_W_SID, slave); + if (rc < 0) + return rc; + return reg_w(sd, R51x_I2C_R_SID, slave + 1); +} + +struct ov_regvals { + __u8 reg; + __u8 val; +}; +struct ov_i2c_regvals { + __u8 reg; + __u8 val; +}; + +static int write_regvals(struct sd *sd, + struct ov_regvals *regvals, + int n) +{ + int rc; + + while (--n >= 0) { + rc = reg_w(sd, regvals->reg, regvals->val); + if (rc < 0) + return rc; + regvals++; + } + return 0; +} + +static int write_i2c_regvals(struct sd *sd, + struct ov_i2c_regvals *regvals, + int n) +{ + int rc; + + while (--n >= 0) { + rc = i2c_w(sd, regvals->reg, regvals->val); + if (rc < 0) + return rc; + regvals++; + } + return 0; +} + +/**************************************************************************** + * + * OV511 and sensor configuration + * + ***************************************************************************/ + +/* This initializes the OV8110, OV8610 sensor. The OV8110 uses + * the same register settings as the OV8610, since they are very similar. + */ +static int ov8xx0_configure(struct sd *sd) +{ + int rc; + static struct ov_i2c_regvals norm_8610[] = { + { 0x12, 0x80 }, + { 0x00, 0x00 }, + { 0x01, 0x80 }, + { 0x02, 0x80 }, + { 0x03, 0xc0 }, + { 0x04, 0x30 }, + { 0x05, 0x30 }, /* was 0x10, new from windrv 090403 */ + { 0x06, 0x70 }, /* was 0x80, new from windrv 090403 */ + { 0x0a, 0x86 }, + { 0x0b, 0xb0 }, + { 0x0c, 0x20 }, + { 0x0d, 0x20 }, + { 0x11, 0x01 }, + { 0x12, 0x25 }, + { 0x13, 0x01 }, + { 0x14, 0x04 }, + { 0x15, 0x01 }, /* Lin and Win think different about UV order */ + { 0x16, 0x03 }, + { 0x17, 0x38 }, /* was 0x2f, new from windrv 090403 */ + { 0x18, 0xea }, /* was 0xcf, new from windrv 090403 */ + { 0x19, 0x02 }, /* was 0x06, new from windrv 090403 */ + { 0x1a, 0xf5 }, + { 0x1b, 0x00 }, + { 0x20, 0xd0 }, /* was 0x90, new from windrv 090403 */ + { 0x23, 0xc0 }, /* was 0x00, new from windrv 090403 */ + { 0x24, 0x30 }, /* was 0x1d, new from windrv 090403 */ + { 0x25, 0x50 }, /* was 0x57, new from windrv 090403 */ + { 0x26, 0xa2 }, + { 0x27, 0xea }, + { 0x28, 0x00 }, + { 0x29, 0x00 }, + { 0x2a, 0x80 }, + { 0x2b, 0xc8 }, /* was 0xcc, new from windrv 090403 */ + { 0x2c, 0xac }, + { 0x2d, 0x45 }, /* was 0xd5, new from windrv 090403 */ + { 0x2e, 0x80 }, + { 0x2f, 0x14 }, /* was 0x01, new from windrv 090403 */ + { 0x4c, 0x00 }, + { 0x4d, 0x30 }, /* was 0x10, new from windrv 090403 */ + { 0x60, 0x02 }, /* was 0x01, new from windrv 090403 */ + { 0x61, 0x00 }, /* was 0x09, new from windrv 090403 */ + { 0x62, 0x5f }, /* was 0xd7, new from windrv 090403 */ + { 0x63, 0xff }, + { 0x64, 0x53 }, /* new windrv 090403 says 0x57, + * maybe thats wrong */ + { 0x65, 0x00 }, + { 0x66, 0x55 }, + { 0x67, 0xb0 }, + { 0x68, 0xc0 }, /* was 0xaf, new from windrv 090403 */ + { 0x69, 0x02 }, + { 0x6a, 0x22 }, + { 0x6b, 0x00 }, + { 0x6c, 0x99 }, /* was 0x80, old windrv says 0x00, but + deleting bit7 colors the first images red */ + { 0x6d, 0x11 }, /* was 0x00, new from windrv 090403 */ + { 0x6e, 0x11 }, /* was 0x00, new from windrv 090403 */ + { 0x6f, 0x01 }, + { 0x70, 0x8b }, + { 0x71, 0x00 }, + { 0x72, 0x14 }, + { 0x73, 0x54 }, + { 0x74, 0x00 },/* 0x60? - was 0x00, new from windrv 090403 */ + { 0x75, 0x0e }, + { 0x76, 0x02 }, /* was 0x02, new from windrv 090403 */ + { 0x77, 0xff }, + { 0x78, 0x80 }, + { 0x79, 0x80 }, + { 0x7a, 0x80 }, + { 0x7b, 0x10 }, /* was 0x13, new from windrv 090403 */ + { 0x7c, 0x00 }, + { 0x7d, 0x08 }, /* was 0x09, new from windrv 090403 */ + { 0x7e, 0x08 }, /* was 0xc0, new from windrv 090403 */ + { 0x7f, 0xfb }, + { 0x80, 0x28 }, + { 0x81, 0x00 }, + { 0x82, 0x23 }, + { 0x83, 0x0b }, + { 0x84, 0x00 }, + { 0x85, 0x62 }, /* was 0x61, new from windrv 090403 */ + { 0x86, 0xc9 }, + { 0x87, 0x00 }, + { 0x88, 0x00 }, + { 0x89, 0x01 }, + { 0x12, 0x20 }, + { 0x12, 0x25 }, /* was 0x24, new from windrv 090403 */ + }; + + PDEBUG(D_PROBE, "starting ov8xx0 configuration"); + + if (init_ov_sensor(sd) < 0) + PDEBUG(D_ERR|D_PROBE, "Failed to read sensor ID"); + else + PDEBUG(D_PROBE, "OV86x0 initialized"); + + /* Detect sensor (sub)type */ + rc = i2c_r(sd, OV7610_REG_COM_I); + if (rc < 0) { + PDEBUG(D_ERR, "Error detecting sensor type"); + return -1; + } + if ((rc & 3) == 1) { + PDEBUG(D_PROBE, "Sensor is an OV8610"); + sd->sensor = SEN_OV8610; + } else { + PDEBUG(D_ERR, "Unknown image sensor version: %d", rc & 3); + return -1; + } + PDEBUG(D_PROBE, "Writing 8610 registers"); + if (write_i2c_regvals(sd, + norm_8610, + sizeof norm_8610 / sizeof norm_8610[0])) + return -1; + + /* Set sensor-specific vars */ + sd->maxwidth = 640; + sd->maxheight = 480; + return 0; +} + +/* This initializes the OV7610, OV7620, or OV76BE sensor. The OV76BE uses + * the same register settings as the OV7610, since they are very similar. + */ +static int ov7xx0_configure(struct sd *sd) +{ + int rc, high, low; + + /* Lawrence Glaister reports: + * + * Register 0x0f in the 7610 has the following effects: + * + * 0x85 (AEC method 1): Best overall, good contrast range + * 0x45 (AEC method 2): Very overexposed + * 0xa5 (spec sheet default): Ok, but the black level is + * shifted resulting in loss of contrast + * 0x05 (old driver setting): very overexposed, too much + * contrast + */ + static struct ov_i2c_regvals norm_7610[] = { + { 0x10, 0xff }, + { 0x16, 0x06 }, + { 0x28, 0x24 }, + { 0x2b, 0xac }, + { 0x12, 0x00 }, + { 0x38, 0x81 }, + { 0x28, 0x24 }, /* 0c */ + { 0x0f, 0x85 }, /* lg's setting */ + { 0x15, 0x01 }, + { 0x20, 0x1c }, + { 0x23, 0x2a }, + { 0x24, 0x10 }, + { 0x25, 0x8a }, + { 0x26, 0xa2 }, + { 0x27, 0xc2 }, + { 0x2a, 0x04 }, + { 0x2c, 0xfe }, + { 0x2d, 0x93 }, + { 0x30, 0x71 }, + { 0x31, 0x60 }, + { 0x32, 0x26 }, + { 0x33, 0x20 }, + { 0x34, 0x48 }, + { 0x12, 0x24 }, + { 0x11, 0x01 }, + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, + }; + + static struct ov_i2c_regvals norm_7620[] = { + { 0x00, 0x00 }, /* gain */ + { 0x01, 0x80 }, /* blue gain */ + { 0x02, 0x80 }, /* red gain */ + { 0x03, 0xc0 }, /* OV7670_REG_VREF */ + { 0x06, 0x60 }, + { 0x07, 0x00 }, + { 0x0c, 0x24 }, + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, + { 0x11, 0x01 }, + { 0x12, 0x24 }, + { 0x13, 0x01 }, + { 0x14, 0x84 }, + { 0x15, 0x01 }, + { 0x16, 0x03 }, + { 0x17, 0x2f }, + { 0x18, 0xcf }, + { 0x19, 0x06 }, + { 0x1a, 0xf5 }, + { 0x1b, 0x00 }, + { 0x20, 0x18 }, + { 0x21, 0x80 }, + { 0x22, 0x80 }, + { 0x23, 0x00 }, + { 0x26, 0xa2 }, + { 0x27, 0xea }, + { 0x28, 0x20 }, + { 0x29, 0x00 }, + { 0x2a, 0x10 }, + { 0x2b, 0x00 }, + { 0x2c, 0x88 }, + { 0x2d, 0x91 }, + { 0x2e, 0x80 }, + { 0x2f, 0x44 }, + { 0x60, 0x27 }, + { 0x61, 0x02 }, + { 0x62, 0x5f }, + { 0x63, 0xd5 }, + { 0x64, 0x57 }, + { 0x65, 0x83 }, + { 0x66, 0x55 }, + { 0x67, 0x92 }, + { 0x68, 0xcf }, + { 0x69, 0x76 }, + { 0x6a, 0x22 }, + { 0x6b, 0x00 }, + { 0x6c, 0x02 }, + { 0x6d, 0x44 }, + { 0x6e, 0x80 }, + { 0x6f, 0x1d }, + { 0x70, 0x8b }, + { 0x71, 0x00 }, + { 0x72, 0x14 }, + { 0x73, 0x54 }, + { 0x74, 0x00 }, + { 0x75, 0x8e }, + { 0x76, 0x00 }, + { 0x77, 0xff }, + { 0x78, 0x80 }, + { 0x79, 0x80 }, + { 0x7a, 0x80 }, + { 0x7b, 0xe2 }, + { 0x7c, 0x00 }, + }; + + /* 7640 and 7648. The defaults should be OK for most registers. */ + static struct ov_i2c_regvals norm_7640[] = { + { 0x12, 0x80 }, + { 0x12, 0x14 }, + }; + + /* 7670. Defaults taken from OmniVision provided data, + * as provided by Jonathan Corbet of OLPC */ + static struct ov_i2c_regvals norm_7670[] = { + { OV7670_REG_COM7, OV7670_COM7_RESET }, + { OV7670_REG_TSLB, 0x04 }, /* OV */ + { OV7670_REG_COM7, OV7670_COM7_FMT_VGA }, /* VGA */ + { OV7670_REG_CLKRC, 0x1 }, + /* + * Set the hardware window. These values from OV don't entirely + * make sense - hstop is less than hstart. But they work... + */ + { OV7670_REG_HSTART, 0x13 }, { OV7670_REG_HSTOP, 0x01 }, + { OV7670_REG_HREF, 0xb6 }, { OV7670_REG_VSTART, 0x02 }, + { OV7670_REG_VSTOP, 0x7a }, { OV7670_REG_VREF, 0x0a }, + + { OV7670_REG_COM3, 0 }, { OV7670_REG_COM14, 0 }, + /* Mystery scaling numbers */ + { 0x70, 0x3a }, { 0x71, 0x35 }, + { 0x72, 0x11 }, { 0x73, 0xf0 }, + { 0xa2, 0x02 }, +/* jfm */ +/* { OV7670_REG_COM10, 0x0 }, */ + + /* Gamma curve values */ + { 0x7a, 0x20 }, +/* jfm:win 7b=1c */ + { 0x7b, 0x10 }, +/* jfm:win 7c=28 */ + { 0x7c, 0x1e }, +/* jfm:win 7d=3c */ + { 0x7d, 0x35 }, + { 0x7e, 0x5a }, { 0x7f, 0x69 }, + { 0x80, 0x76 }, { 0x81, 0x80 }, + { 0x82, 0x88 }, { 0x83, 0x8f }, + { 0x84, 0x96 }, { 0x85, 0xa3 }, + { 0x86, 0xaf }, { 0x87, 0xc4 }, + { 0x88, 0xd7 }, { 0x89, 0xe8 }, + + /* AGC and AEC parameters. Note we start by disabling those features, + then turn them only after tweaking the values. */ + { OV7670_REG_COM8, OV7670_COM8_FASTAEC + | OV7670_COM8_AECSTEP + | OV7670_COM8_BFILT }, + { OV7670_REG_GAIN, 0 }, { OV7670_REG_AECH, 0 }, + { OV7670_REG_COM4, 0x40 }, /* magic reserved bit */ +/* jfm:win 14=38 */ + { OV7670_REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */ + { OV7670_REG_BD50MAX, 0x05 }, { OV7670_REG_BD60MAX, 0x07 }, + { OV7670_REG_AEW, 0x95 }, { OV7670_REG_AEB, 0x33 }, + { OV7670_REG_VPT, 0xe3 }, { OV7670_REG_HAECC1, 0x78 }, + { OV7670_REG_HAECC2, 0x68 }, +/* jfm:win a1=0b */ + { 0xa1, 0x03 }, /* magic */ + { OV7670_REG_HAECC3, 0xd8 }, { OV7670_REG_HAECC4, 0xd8 }, + { OV7670_REG_HAECC5, 0xf0 }, { OV7670_REG_HAECC6, 0x90 }, + { OV7670_REG_HAECC7, 0x94 }, + { OV7670_REG_COM8, OV7670_COM8_FASTAEC + | OV7670_COM8_AECSTEP + | OV7670_COM8_BFILT + | OV7670_COM8_AGC + | OV7670_COM8_AEC }, + + /* Almost all of these are magic "reserved" values. */ + { OV7670_REG_COM5, 0x61 }, { OV7670_REG_COM6, 0x4b }, + { 0x16, 0x02 }, +/* jfm */ +/* { OV7670_REG_MVFP, 0x07|OV7670_MVFP_MIRROR }, */ + { OV7670_REG_MVFP, 0x07 }, + { 0x21, 0x02 }, { 0x22, 0x91 }, + { 0x29, 0x07 }, { 0x33, 0x0b }, + { 0x35, 0x0b }, { 0x37, 0x1d }, + { 0x38, 0x71 }, { 0x39, 0x2a }, + { OV7670_REG_COM12, 0x78 }, { 0x4d, 0x40 }, + { 0x4e, 0x20 }, { OV7670_REG_GFIX, 0 }, + { 0x6b, 0x4a }, { 0x74, 0x10 }, + { 0x8d, 0x4f }, { 0x8e, 0 }, + { 0x8f, 0 }, { 0x90, 0 }, + { 0x91, 0 }, { 0x96, 0 }, + { 0x9a, 0 }, { 0xb0, 0x84 }, + { 0xb1, 0x0c }, { 0xb2, 0x0e }, + { 0xb3, 0x82 }, { 0xb8, 0x0a }, + + /* More reserved magic, some of which tweaks white balance */ + { 0x43, 0x0a }, { 0x44, 0xf0 }, + { 0x45, 0x34 }, { 0x46, 0x58 }, + { 0x47, 0x28 }, { 0x48, 0x3a }, + { 0x59, 0x88 }, { 0x5a, 0x88 }, + { 0x5b, 0x44 }, { 0x5c, 0x67 }, + { 0x5d, 0x49 }, { 0x5e, 0x0e }, + { 0x6c, 0x0a }, { 0x6d, 0x55 }, + { 0x6e, 0x11 }, { 0x6f, 0x9f }, + /* "9e for advance AWB" */ + { 0x6a, 0x40 }, { OV7670_REG_BLUE, 0x40 }, + { OV7670_REG_RED, 0x60 }, + { OV7670_REG_COM8, OV7670_COM8_FASTAEC + | OV7670_COM8_AECSTEP + | OV7670_COM8_BFILT + | OV7670_COM8_AGC + | OV7670_COM8_AEC + | OV7670_COM8_AWB }, + + /* Matrix coefficients */ + { 0x4f, 0x80 }, { 0x50, 0x80 }, + { 0x51, 0 }, { 0x52, 0x22 }, + { 0x53, 0x5e }, { 0x54, 0x80 }, + { 0x58, 0x9e }, + + { OV7670_REG_COM16, OV7670_COM16_AWBGAIN }, + { OV7670_REG_EDGE, 0 }, + { 0x75, 0x05 }, { 0x76, 0xe1 }, + { 0x4c, 0 }, { 0x77, 0x01 }, + { OV7670_REG_COM13, 0xc3 }, { 0x4b, 0x09 }, + { 0xc9, 0x60 }, { OV7670_REG_COM16, 0x38 }, + { 0x56, 0x40 }, + + { 0x34, 0x11 }, + { OV7670_REG_COM11, OV7670_COM11_EXP|OV7670_COM11_HZAUTO }, + { 0xa4, 0x88 }, { 0x96, 0 }, + { 0x97, 0x30 }, { 0x98, 0x20 }, + { 0x99, 0x30 }, { 0x9a, 0x84 }, + { 0x9b, 0x29 }, { 0x9c, 0x03 }, + { 0x9d, 0x4c }, { 0x9e, 0x3f }, + { 0x78, 0x04 }, + + /* Extra-weird stuff. Some sort of multiplexor register */ + { 0x79, 0x01 }, { 0xc8, 0xf0 }, + { 0x79, 0x0f }, { 0xc8, 0x00 }, + { 0x79, 0x10 }, { 0xc8, 0x7e }, + { 0x79, 0x0a }, { 0xc8, 0x80 }, + { 0x79, 0x0b }, { 0xc8, 0x01 }, + { 0x79, 0x0c }, { 0xc8, 0x0f }, + { 0x79, 0x0d }, { 0xc8, 0x20 }, + { 0x79, 0x09 }, { 0xc8, 0x80 }, + { 0x79, 0x02 }, { 0xc8, 0xc0 }, + { 0x79, 0x03 }, { 0xc8, 0x40 }, + { 0x79, 0x05 }, { 0xc8, 0x30 }, + { 0x79, 0x26 }, + + /* Format YUV422 */ + { OV7670_REG_COM7, OV7670_COM7_YUV }, /* Selects YUV mode */ + { OV7670_REG_RGB444, 0 }, /* No RGB444 please */ + { OV7670_REG_COM1, 0 }, + { OV7670_REG_COM15, OV7670_COM15_R00FF }, + { OV7670_REG_COM9, 0x18 }, + /* 4x gain ceiling; 0x8 is reserved bit */ + { 0x4f, 0x80 }, /* "matrix coefficient 1" */ + { 0x50, 0x80 }, /* "matrix coefficient 2" */ + { 0x52, 0x22 }, /* "matrix coefficient 4" */ + { 0x53, 0x5e }, /* "matrix coefficient 5" */ + { 0x54, 0x80 }, /* "matrix coefficient 6" */ + { OV7670_REG_COM13, OV7670_COM13_GAMMA|OV7670_COM13_UVSAT }, +}; + + PDEBUG(D_PROBE, "starting OV7xx0 configuration"); + +/* jfm:already done? */ + if (init_ov_sensor(sd) < 0) + PDEBUG(D_ERR, "Failed to read sensor ID"); + else + PDEBUG(D_PROBE, "OV7xx0 initialized"); + + /* Detect sensor (sub)type */ + rc = i2c_r(sd, OV7610_REG_COM_I); + + /* add OV7670 here + * it appears to be wrongly detected as a 7610 by default */ + if (rc < 0) { + PDEBUG(D_ERR, "Error detecting sensor type"); + return -1; + } + if ((rc & 3) == 3) { + /* quick hack to make OV7670s work */ + high = i2c_r(sd, 0x0a); + low = i2c_r(sd, 0x0b); + /* info("%x, %x", high, low); */ + if (high == 0x76 && low == 0x73) { + PDEBUG(D_PROBE, "Sensor is an OV7670"); + sd->sensor = SEN_OV7670; + } else { + PDEBUG(D_PROBE, "Sensor is an OV7610"); + sd->sensor = SEN_OV7610; + } + } else if ((rc & 3) == 1) { + /* I don't know what's different about the 76BE yet. */ + if (i2c_r(sd, 0x15) & 1) + PDEBUG(D_PROBE, "Sensor is an OV7620AE"); + else + PDEBUG(D_PROBE, "Sensor is an OV76BE"); + + /* OV511+ will return all zero isoc data unless we + * configure the sensor as a 7620. Someone needs to + * find the exact reg. setting that causes this. */ + sd->sensor = SEN_OV76BE; + } else if ((rc & 3) == 0) { + /* try to read product id registers */ + high = i2c_r(sd, 0x0a); + if (high < 0) { + PDEBUG(D_ERR, "Error detecting camera chip PID"); + return high; + } + low = i2c_r(sd, 0x0b); + if (low < 0) { + PDEBUG(D_ERR, "Error detecting camera chip VER"); + return low; + } + if (high == 0x76) { + if (low == 0x30) { + PDEBUG(D_PROBE, "Sensor is an OV7630/OV7635"); + sd->sensor = SEN_OV7630; + } else if (low == 0x40) { + PDEBUG(D_PROBE, "Sensor is an OV7645"); + sd->sensor = SEN_OV7640; /* FIXME */ + } else if (low == 0x45) { + PDEBUG(D_PROBE, "Sensor is an OV7645B"); + sd->sensor = SEN_OV7640; /* FIXME */ + } else if (low == 0x48) { + PDEBUG(D_PROBE, "Sensor is an OV7648"); + sd->sensor = SEN_OV7640; /* FIXME */ + } else { + PDEBUG(D_PROBE, "Unknown sensor: 0x76%X", low); + return -1; + } + } else { + PDEBUG(D_PROBE, "Sensor is an OV7620"); + sd->sensor = SEN_OV7620; + } + } else { + PDEBUG(D_ERR, "Unknown image sensor version: %d", rc & 3); + return -1; + } + + if (sd->sensor == SEN_OV7620) { + PDEBUG(D_PROBE, "Writing 7620 registers"); + if (write_i2c_regvals(sd, norm_7620, + sizeof norm_7620 / sizeof norm_7620[0])) + return -1; + } else if (sd->sensor == SEN_OV7630) { + PDEBUG(D_ERR, "7630 is not supported by this driver version"); + return -1; + } else if (sd->sensor == SEN_OV7640) { + PDEBUG(D_PROBE, "Writing 7640 registers"); + if (write_i2c_regvals(sd, norm_7640, + sizeof norm_7640 / sizeof norm_7640[0])) + return -1; + } else if (sd->sensor == SEN_OV7670) { + PDEBUG(D_PROBE, "Writing 7670 registers"); + if (write_i2c_regvals(sd, norm_7670, + sizeof norm_7670 / sizeof norm_7670[0])) + return -1; + } else { + PDEBUG(D_PROBE, "Writing 7610 registers"); + if (write_i2c_regvals(sd, norm_7610, + sizeof norm_7610 / sizeof norm_7610[0])) + return -1; + } + + /* Set sensor-specific vars */ + sd->maxwidth = 640; + sd->maxheight = 480; + return 0; +} + +/* This initializes the OV6620, OV6630, OV6630AE, or OV6630AF sensor. */ +static int ov6xx0_configure(struct sd *sd) +{ + int rc; + static struct ov_i2c_regvals norm_6x20[] = { + { 0x12, 0x80 }, /* reset */ + { 0x11, 0x01 }, + { 0x03, 0x60 }, + { 0x05, 0x7f }, /* For when autoadjust is off */ + { 0x07, 0xa8 }, + /* The ratio of 0x0c and 0x0d controls the white point */ + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, + { 0x0f, 0x15 }, /* COMS */ + { 0x10, 0x75 }, /* AEC Exposure time */ + { 0x12, 0x24 }, /* Enable AGC */ + { 0x14, 0x04 }, + /* 0x16: 0x06 helps frame stability with moving objects */ + { 0x16, 0x06 }, +/* { 0x20, 0x30 }, * Aperture correction enable */ + { 0x26, 0xb2 }, /* BLC enable */ + /* 0x28: 0x05 Selects RGB format if RGB on */ + { 0x28, 0x05 }, + { 0x2a, 0x04 }, /* Disable framerate adjust */ +/* { 0x2b, 0xac }, * Framerate; Set 2a[7] first */ + { 0x2d, 0x99 }, + { 0x33, 0xa0 }, /* Color Processing Parameter */ + { 0x34, 0xd2 }, /* Max A/D range */ + { 0x38, 0x8b }, + { 0x39, 0x40 }, + + { 0x3c, 0x39 }, /* Enable AEC mode changing */ + { 0x3c, 0x3c }, /* Change AEC mode */ + { 0x3c, 0x24 }, /* Disable AEC mode changing */ + + { 0x3d, 0x80 }, + /* These next two registers (0x4a, 0x4b) are undocumented. + * They control the color balance */ + { 0x4a, 0x80 }, + { 0x4b, 0x80 }, + { 0x4d, 0xd2 }, /* This reduces noise a bit */ + { 0x4e, 0xc1 }, + { 0x4f, 0x04 }, +/* Do 50-53 have any effect? */ +/* Toggle 0x12[2] off and on here? */ + }; + + static struct ov_i2c_regvals norm_6x30[] = { + { 0x12, 0x80 }, /* Reset */ + { 0x00, 0x1f }, /* Gain */ + { 0x01, 0x99 }, /* Blue gain */ + { 0x02, 0x7c }, /* Red gain */ + { 0x03, 0xc0 }, /* Saturation */ + { 0x05, 0x0a }, /* Contrast */ + { 0x06, 0x95 }, /* Brightness */ + { 0x07, 0x2d }, /* Sharpness */ + { 0x0c, 0x20 }, + { 0x0d, 0x20 }, + { 0x0e, 0x20 }, + { 0x0f, 0x05 }, + { 0x10, 0x9a }, + { 0x11, 0x00 }, /* Pixel clock = fastest */ + { 0x12, 0x24 }, /* Enable AGC and AWB */ + { 0x13, 0x21 }, + { 0x14, 0x80 }, + { 0x15, 0x01 }, + { 0x16, 0x03 }, + { 0x17, 0x38 }, + { 0x18, 0xea }, + { 0x19, 0x04 }, + { 0x1a, 0x93 }, + { 0x1b, 0x00 }, + { 0x1e, 0xc4 }, + { 0x1f, 0x04 }, + { 0x20, 0x20 }, + { 0x21, 0x10 }, + { 0x22, 0x88 }, + { 0x23, 0xc0 }, /* Crystal circuit power level */ + { 0x25, 0x9a }, /* Increase AEC black ratio */ + { 0x26, 0xb2 }, /* BLC enable */ + { 0x27, 0xa2 }, + { 0x28, 0x00 }, + { 0x29, 0x00 }, + { 0x2a, 0x84 }, /* 60 Hz power */ + { 0x2b, 0xa8 }, /* 60 Hz power */ + { 0x2c, 0xa0 }, + { 0x2d, 0x95 }, /* Enable auto-brightness */ + { 0x2e, 0x88 }, + { 0x33, 0x26 }, + { 0x34, 0x03 }, + { 0x36, 0x8f }, + { 0x37, 0x80 }, + { 0x38, 0x83 }, + { 0x39, 0x80 }, + { 0x3a, 0x0f }, + { 0x3b, 0x3c }, + { 0x3c, 0x1a }, + { 0x3d, 0x80 }, + { 0x3e, 0x80 }, + { 0x3f, 0x0e }, + { 0x40, 0x00 }, /* White bal */ + { 0x41, 0x00 }, /* White bal */ + { 0x42, 0x80 }, + { 0x43, 0x3f }, /* White bal */ + { 0x44, 0x80 }, + { 0x45, 0x20 }, + { 0x46, 0x20 }, + { 0x47, 0x80 }, + { 0x48, 0x7f }, + { 0x49, 0x00 }, + { 0x4a, 0x00 }, + { 0x4b, 0x80 }, + { 0x4c, 0xd0 }, + { 0x4d, 0x10 }, /* U = 0.563u, V = 0.714v */ + { 0x4e, 0x40 }, + { 0x4f, 0x07 }, /* UV avg., col. killer: max */ + { 0x50, 0xff }, + { 0x54, 0x23 }, /* Max AGC gain: 18dB */ + { 0x55, 0xff }, + { 0x56, 0x12 }, + { 0x57, 0x81 }, + { 0x58, 0x75 }, + { 0x59, 0x01 }, /* AGC dark current comp.: +1 */ + { 0x5a, 0x2c }, + { 0x5b, 0x0f }, /* AWB chrominance levels */ + { 0x5c, 0x10 }, + { 0x3d, 0x80 }, + { 0x27, 0xa6 }, + { 0x12, 0x20 }, /* Toggle AWB */ + { 0x12, 0x24 }, + }; + + PDEBUG(D_PROBE, "starting sensor configuration"); + + if (init_ov_sensor(sd) < 0) { + PDEBUG(D_ERR, "Failed to read sensor ID."); + return -1; + } + PDEBUG(D_PROBE, "OV6xx0 sensor detected"); + + /* Detect sensor (sub)type */ + rc = i2c_r(sd, OV7610_REG_COM_I); + if (rc < 0) { + PDEBUG(D_ERR, "Error detecting sensor type"); + return -1; + } + + /* Ugh. The first two bits are the version bits, but + * the entire register value must be used. I guess OVT + * underestimated how many variants they would make. */ + if (rc == 0x00) { + sd->sensor = SEN_OV6630; + PDEBUG(D_ERR, + "WARNING: Sensor is an OV66308. Your camera may have"); + PDEBUG(D_ERR, "been misdetected in previous driver versions."); + } else if (rc == 0x01) { + sd->sensor = SEN_OV6620; + PDEBUG(D_PROBE, "Sensor is an OV6620"); + } else if (rc == 0x02) { + sd->sensor = SEN_OV6630; + PDEBUG(D_PROBE, "Sensor is an OV66308AE"); + } else if (rc == 0x03) { + sd->sensor = SEN_OV6630; + PDEBUG(D_PROBE, "Sensor is an OV66308AF"); + } else if (rc == 0x90) { + sd->sensor = SEN_OV6630; + PDEBUG(D_ERR, + "WARNING: Sensor is an OV66307. Your camera may have"); + PDEBUG(D_ERR, "been misdetected in previous driver versions."); + } else { + PDEBUG(D_ERR, "FATAL: Unknown sensor version: 0x%02x", rc); + return -1; + } + + /* Set sensor-specific vars */ + sd->maxwidth = 352; + sd->maxheight = 288; + + if (sd->sensor == SEN_OV6620) { + PDEBUG(D_PROBE, "Writing 6x20 registers"); + if (write_i2c_regvals(sd, norm_6x20, + sizeof norm_6x20 / sizeof norm_6x20[0])) + return -1; + } else { + PDEBUG(D_PROBE, "Writing 6x30 registers"); + if (write_i2c_regvals(sd, norm_6x30, + sizeof norm_6x30 / sizeof norm_6x30[0])) + return -1; + } + return 0; +} + +/* Turns on or off the LED. Only has an effect with OV511+/OV518(+)/OV519 */ +static void ov51x_led_control(struct sd *sd, int on) +{ + PDEBUG(D_STREAM, "LED (%s)", on ? "on" : "off"); + +/* if (sd->bridge == BRG_OV511PLUS) */ +/* reg_w(sd, R511_SYS_LED_CTL, on ? 1 : 0); */ +/* else if (sd->bridge == BRG_OV519) */ + reg_w_mask(sd, OV519_GPIO_DATA_OUT0, !on, 1); /* 0 / 1 */ +/* else if (sd->bclass == BCL_OV518) */ +/* reg_w_mask(sd, R518_GPIO_OUT, on ? 0x02 : 0x00, 0x02); */ +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + +/* (from ov519_configure) */ + static struct ov_regvals init_519[] = { + { 0x5a, 0x6d }, /* EnableSystem */ +/* jfm trace usbsnoop3-1.txt */ +/* jfm 53 = fb */ + { 0x53, 0x9b }, + { 0x54, 0xff }, /* set bit2 to enable jpeg */ + { 0x5d, 0x03 }, + { 0x49, 0x01 }, + { 0x48, 0x00 }, + /* Set LED pin to output mode. Bit 4 must be cleared or sensor + * detection will fail. This deserves further investigation. */ + { OV519_GPIO_IO_CTRL0, 0xee }, + { 0x51, 0x0f }, /* SetUsbInit */ + { 0x51, 0x00 }, + { 0x22, 0x00 }, + /* windows reads 0x55 at this point*/ + }; + + if (write_regvals(sd, init_519, + sizeof init_519 / sizeof init_519[0])) + goto error; +/* jfm: not seen in windows trace */ + if (ov519_init_compression(sd)) + goto error; + ov51x_led_control(sd, 0); /* turn LED off */ + + /* Test for 76xx */ + sd->primary_i2c_slave = OV7xx0_SID; + if (ov51x_set_slave_ids(sd, OV7xx0_SID) < 0) + goto error; + + /* The OV519 must be more aggressive about sensor detection since + * I2C write will never fail if the sensor is not present. We have + * to try to initialize the sensor to detect its presence */ + if (init_ov_sensor(sd) < 0) { + /* Test for 6xx0 */ + sd->primary_i2c_slave = OV6xx0_SID; + if (ov51x_set_slave_ids(sd, OV6xx0_SID) < 0) + goto error; + + if (init_ov_sensor(sd) < 0) { + /* Test for 8xx0 */ + sd->primary_i2c_slave = OV8xx0_SID; + if (ov51x_set_slave_ids(sd, OV8xx0_SID) < 0) + goto error; + + if (init_ov_sensor(sd) < 0) { + PDEBUG(D_ERR, + "Can't determine sensor slave IDs"); + goto error; + } else { + if (ov8xx0_configure(sd) < 0) { + PDEBUG(D_ERR, + "Failed to configure OV8xx0 sensor"); + goto error; + } + } + } else { + if (ov6xx0_configure(sd) < 0) { + PDEBUG(D_ERR, "Failed to configure OV6xx0"); + goto error; + } + } + } else { + if (ov7xx0_configure(sd) < 0) { + PDEBUG(D_ERR, "Failed to configure OV7xx0"); + goto error; + } + } + + cam = &gspca_dev->cam; + cam->epaddr = OV511_ENDPOINT_ADDRESS; + if (sd->maxwidth == 640) { + cam->cam_mode = vga_mode; + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + } else { + cam->cam_mode = sif_mode; + cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; + } + cam->dev_name = (char *) id->driver_info; + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; + sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value; + return 0; +error: + PDEBUG(D_ERR, "OV519 Config failed"); + return -EBUSY; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + return 0; +} + +/* Sets up the OV519 with the given image parameters + * + * OV519 needs a completely different approach, until we can figure out what + * the individual registers do. + * + * Do not put any sensor-specific code in here (including I2C I/O functions) + */ +static int ov519_mode_init_regs(struct sd *sd, + int width, int height) +{ + static struct ov_regvals mode_init_519_ov7670[] = { + { 0x5d, 0x03 }, /* Turn off suspend mode */ + { 0x53, 0x9f }, /* was 9b in 1.65-1.08 */ + { 0x54, 0x0f }, /* bit2 (jpeg enable) */ + { 0xa2, 0x20 }, /* a2-a5 are undocumented */ + { 0xa3, 0x18 }, + { 0xa4, 0x04 }, + { 0xa5, 0x28 }, + { 0x37, 0x00 }, /* SetUsbInit */ + { 0x55, 0x02 }, /* 4.096 Mhz audio clock */ + /* Enable both fields, YUV Input, disable defect comp (why?) */ + { 0x20, 0x0c }, + { 0x21, 0x38 }, + { 0x22, 0x1d }, + { 0x17, 0x50 }, /* undocumented */ + { 0x37, 0x00 }, /* undocumented */ + { 0x40, 0xff }, /* I2C timeout counter */ + { 0x46, 0x00 }, /* I2C clock prescaler */ + { 0x59, 0x04 }, /* new from windrv 090403 */ + { 0xff, 0x00 }, /* undocumented */ + /* windows reads 0x55 at this point, why? */ + }; + + static struct ov_regvals mode_init_519[] = { + { 0x5d, 0x03 }, /* Turn off suspend mode */ + { 0x53, 0x9f }, /* was 9b in 1.65-1.08 */ + { 0x54, 0x0f }, /* bit2 (jpeg enable) */ + { 0xa2, 0x20 }, /* a2-a5 are undocumented */ + { 0xa3, 0x18 }, + { 0xa4, 0x04 }, + { 0xa5, 0x28 }, + { 0x37, 0x00 }, /* SetUsbInit */ + { 0x55, 0x02 }, /* 4.096 Mhz audio clock */ + /* Enable both fields, YUV Input, disable defect comp (why?) */ + { 0x22, 0x1d }, + { 0x17, 0x50 }, /* undocumented */ + { 0x37, 0x00 }, /* undocumented */ + { 0x40, 0xff }, /* I2C timeout counter */ + { 0x46, 0x00 }, /* I2C clock prescaler */ + { 0x59, 0x04 }, /* new from windrv 090403 */ + { 0xff, 0x00 }, /* undocumented */ + /* windows reads 0x55 at this point, why? */ + }; + +/* int hi_res; */ + + PDEBUG(D_CONF, "mode init %dx%d", width, height); + +/* if (width >= 800 && height >= 600) + hi_res = 1; + else + hi_res = 0; */ + +/* if (ov51x_stop(sd) < 0) + return -EIO; */ + + /******** Set the mode ********/ + if (sd->sensor != SEN_OV7670) { + if (write_regvals(sd, mode_init_519, + sizeof mode_init_519 / sizeof mode_init_519[0])) + return -EIO; + } else { + if (write_regvals(sd, mode_init_519_ov7670, + sizeof mode_init_519_ov7670 + / sizeof mode_init_519_ov7670[0])) + return -EIO; + } + + if (sd->sensor == SEN_OV7640) { + /* Select 8-bit input mode */ + reg_w_mask(sd, OV519_CAM_DFR, 0x10, 0x10); + } + + reg_w(sd, OV519_CAM_H_SIZE, width >> 4); + reg_w(sd, OV519_CAM_V_SIZE, height >> 3); + reg_w(sd, OV519_CAM_X_OFFSETL, 0x00); + reg_w(sd, OV519_CAM_X_OFFSETH, 0x00); + reg_w(sd, OV519_CAM_Y_OFFSETL, 0x00); + reg_w(sd, OV519_CAM_Y_OFFSETH, 0x00); + reg_w(sd, OV519_CAM_DIVIDER, 0x00); + reg_w(sd, OV519_CAM_FORMAT, 0x03); /* YUV422 */ + reg_w(sd, 0x26, 0x00); /* Undocumented */ + + /******** Set the framerate ********/ + if (frame_rate > 0) + sd->frame_rate = frame_rate; + +/* FIXME: These are only valid at the max resolution. */ + sd->clockdiv = 0; + if (sd->sensor == SEN_OV7640) { + switch (sd->frame_rate) { +/*jfm: default was 30 fps */ + case 30: + reg_w(sd, 0xa4, 0x0c); + reg_w(sd, 0x23, 0xff); + break; + case 25: + reg_w(sd, 0xa4, 0x0c); + reg_w(sd, 0x23, 0x1f); + break; + case 20: + reg_w(sd, 0xa4, 0x0c); + reg_w(sd, 0x23, 0x1b); + break; + default: +/* case 15: */ + reg_w(sd, 0xa4, 0x04); + reg_w(sd, 0x23, 0xff); + sd->clockdiv = 1; + break; + case 10: + reg_w(sd, 0xa4, 0x04); + reg_w(sd, 0x23, 0x1f); + sd->clockdiv = 1; + break; + case 5: + reg_w(sd, 0xa4, 0x04); + reg_w(sd, 0x23, 0x1b); + sd->clockdiv = 1; + break; + } + } else if (sd->sensor == SEN_OV8610) { + switch (sd->frame_rate) { + default: /* 15 fps */ +/* case 15: */ + reg_w(sd, 0xa4, 0x06); + reg_w(sd, 0x23, 0xff); + break; + case 10: + reg_w(sd, 0xa4, 0x06); + reg_w(sd, 0x23, 0x1f); + break; + case 5: + reg_w(sd, 0xa4, 0x06); + reg_w(sd, 0x23, 0x1b); + break; + } + sd->clockdiv = 0; + } else if (sd->sensor == SEN_OV7670) { /* guesses, based on 7640 */ + PDEBUG(D_STREAM, "Setting framerate to %d fps", + (sd->frame_rate == 0) ? 15 : sd->frame_rate); + switch (sd->frame_rate) { + case 30: + reg_w(sd, 0xa4, 0x10); + reg_w(sd, 0x23, 0xff); + break; + case 20: + reg_w(sd, 0xa4, 0x10); + reg_w(sd, 0x23, 0x1b); + break; + default: /* 15 fps */ +/* case 15: */ + reg_w(sd, 0xa4, 0x10); + reg_w(sd, 0x23, 0xff); + sd->clockdiv = 1; + break; + } + } + +/* if (ov51x_restart(sd) < 0) + return -EIO; */ + + /* Reset it just for good measure */ +/* if (ov51x_reset(sd, OV511_RESET_NOREGS) < 0) + return -EIO; */ + return 0; +} + +static int mode_init_ov_sensor_regs(struct sd *sd, + struct ovsensor_window *win) +{ + int qvga = win->quarter; + + /******** Mode (VGA/QVGA) and sensor specific regs ********/ + switch (sd->sensor) { + case SEN_OV8610: + /* For OV8610 qvga means qsvga */ + i2c_w_mask(sd, OV7610_REG_COM_C, qvga ? (1 << 5) : 0, 1 << 5); + break; + case SEN_OV7610: + i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + break; + case SEN_OV7620: +/* i2c_w(sd, 0x2b, 0x00); */ + i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20); + i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a); + i2c_w(sd, 0x25, qvga ? 0x30 : 0x60); + i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40); + i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0); + i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); + break; + case SEN_OV76BE: +/* i2c_w(sd, 0x2b, 0x00); */ + i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + break; + case SEN_OV7640: +/* i2c_w(sd, 0x2b, 0x00); */ + i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20); +/* i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a); */ +/* i2c_w(sd, 0x25, qvga ? 0x30 : 0x60); */ +/* i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40); */ +/* i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0); */ +/* i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); */ + break; + case SEN_OV7670: + /* set COM7_FMT_VGA or COM7_FMT_QVGA + * do we need to set anything else? + * HSTART etc are set in set_ov_sensor_window itself */ + i2c_w_mask(sd, OV7670_REG_COM7, + qvga ? OV7670_COM7_FMT_QVGA : OV7670_COM7_FMT_VGA, + OV7670_COM7_FMT_MASK); + break; + case SEN_OV6620: + i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + break; + case SEN_OV6630: + i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + break; + default: + return -EINVAL; + } + + /******** Palette-specific regs ********/ +/* Need to do work here for the OV7670 */ + + if (sd->sensor == SEN_OV7610 || sd->sensor == SEN_OV76BE) { + /* not valid on the OV6620/OV7620/6630? */ + i2c_w_mask(sd, 0x0e, 0x00, 0x40); + } + + /* The OV518 needs special treatment. Although both the OV518 + * and the OV6630 support a 16-bit video bus, only the 8 bit Y + * bus is actually used. The UV bus is tied to ground. + * Therefore, the OV6630 needs to be in 8-bit multiplexed + * output mode */ + + /* OV7640 is 8-bit only */ + + if (sd->sensor != SEN_OV6630 && sd->sensor != SEN_OV7640) + i2c_w_mask(sd, 0x13, 0x00, 0x20); +/* } */ + + /******** Clock programming ********/ + /* The OV6620 needs special handling. This prevents the + * severe banding that normally occurs */ + if (sd->sensor == SEN_OV6620) { + + /* Clock down */ + i2c_w(sd, 0x2a, 0x04); + i2c_w(sd, 0x11, win->clockdiv); + i2c_w(sd, 0x2a, 0x84); + /* This next setting is critical. It seems to improve + * the gain or the contrast. The "reserved" bits seem + * to have some effect in this case. */ + i2c_w(sd, 0x2d, 0x85); + } else if (win->clockdiv >= 0) { + i2c_w(sd, 0x11, win->clockdiv); + } + + /******** Special Features ********/ +/* no evidence this is possible with OV7670, either */ + /* Test Pattern */ + if (sd->sensor != SEN_OV7640 && sd->sensor != SEN_OV7670) + i2c_w_mask(sd, 0x12, 0x00, 0x02); + + /* Enable auto white balance */ + if (sd->sensor == SEN_OV7670) + i2c_w_mask(sd, OV7670_REG_COM8, OV7670_COM8_AWB, + OV7670_COM8_AWB); + else + i2c_w_mask(sd, 0x12, 0x04, 0x04); + + /* This will go away as soon as ov51x_mode_init_sensor_regs() */ + /* is fully tested. */ + /* 7620/6620/6630? don't have register 0x35, so play it safe */ + if (sd->sensor == SEN_OV7610 || sd->sensor == SEN_OV76BE) { + if (win->width == 640 /*&& win->height == 480*/) + i2c_w(sd, 0x35, 0x9e); + else + i2c_w(sd, 0x35, 0x1e); + } + return 0; +} + +static int set_ov_sensor_window(struct sd *sd, + struct ovsensor_window *win) +{ + int hwsbase, hwebase, vwsbase, vwebase, hwscale, vwscale; + int ret, hstart, hstop, vstop, vstart; + __u8 v; + + /* The different sensor ICs handle setting up of window differently. + * IF YOU SET IT WRONG, YOU WILL GET ALL ZERO ISOC DATA FROM OV51x!! */ + switch (sd->sensor) { + case SEN_OV8610: + hwsbase = 0x1e; + hwebase = 0x1e; + vwsbase = 0x02; + vwebase = 0x02; + break; + case SEN_OV7610: + case SEN_OV76BE: + hwsbase = 0x38; + hwebase = 0x3a; + vwsbase = vwebase = 0x05; + break; + case SEN_OV6620: + case SEN_OV6630: + hwsbase = 0x38; + hwebase = 0x3a; + vwsbase = 0x05; + vwebase = 0x06; + break; + case SEN_OV7620: + hwsbase = 0x2f; /* From 7620.SET (spec is wrong) */ + hwebase = 0x2f; + vwsbase = vwebase = 0x05; + break; + case SEN_OV7640: + hwsbase = 0x1a; + hwebase = 0x1a; + vwsbase = vwebase = 0x03; + break; + case SEN_OV7670: + /*handling of OV7670 hardware sensor start and stop values + * is very odd, compared to the other OV sensors */ + vwsbase = vwebase = hwebase = hwsbase = 0x00; + break; + default: + return -EINVAL; + } + + switch (sd->sensor) { + case SEN_OV6620: + case SEN_OV6630: + if (win->quarter) { /* QCIF */ + hwscale = 0; + vwscale = 0; + } else { /* CIF */ + hwscale = 1; + vwscale = 1; /* The datasheet says 0; + * it's wrong */ + } + break; + case SEN_OV8610: + if (win->quarter) { /* QSVGA */ + hwscale = 1; + vwscale = 1; + } else { /* SVGA */ + hwscale = 2; + vwscale = 2; + } + break; + default: /* SEN_OV7xx0 */ + if (win->quarter) { /* QVGA */ + hwscale = 1; + vwscale = 0; + } else { /* VGA */ + hwscale = 2; + vwscale = 1; + } + } + + ret = mode_init_ov_sensor_regs(sd, win); + if (ret < 0) + return ret; + + if (sd->sensor == SEN_OV8610) { + i2c_w_mask(sd, 0x2d, 0x05, 0x40); + /* old 0x95, new 0x05 from windrv 090403 */ + /* bits 5-7: reserved */ + i2c_w_mask(sd, 0x28, 0x20, 0x20); + /* bit 5: progressive mode on */ + } + + /* The below is wrong for OV7670s because their window registers + * only store the high bits in 0x17 to 0x1a */ + + /* SRH Use sd->max values instead of requested win values */ + /* SCS Since we're sticking with only the max hardware widths + * for a given mode */ + /* I can hard code this for OV7670s */ + /* Yes, these numbers do look odd, but they're tested and work! */ + if (sd->sensor == SEN_OV7670) { + if (win->quarter) { /* QVGA from ov7670.c by + * Jonathan Corbet */ + hstart = 164; + hstop = 20; + vstart = 14; + vstop = 494; + } else { /* VGA */ + hstart = 158; + hstop = 14; + vstart = 10; + vstop = 490; + } + /* OV7670 hardware window registers are split across + * multiple locations */ + i2c_w(sd, OV7670_REG_HSTART, (hstart >> 3) & 0xff); + i2c_w(sd, OV7670_REG_HSTOP, (hstop >> 3) & 0xff); + v = i2c_r(sd, OV7670_REG_HREF); + v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x07); + msleep(10); /* need to sleep between read and write to + * same reg! */ + i2c_w(sd, OV7670_REG_HREF, v); + + i2c_w(sd, OV7670_REG_VSTART, (vstart >> 2) & 0xff); + i2c_w(sd, OV7670_REG_VSTOP, (vstop >> 2) & 0xff); + v = i2c_r(sd, OV7670_REG_VREF); + v = (v & 0xc0) | ((vstop & 0x3) << 2) | (vstart & 0x03); + msleep(10); /* need to sleep between read and write to + * same reg! */ + i2c_w(sd, OV7670_REG_VREF, v); + + } else { + i2c_w(sd, 0x17, hwsbase + (win->x >> hwscale)); + i2c_w(sd, 0x18, hwebase + ((win->x + win->width) >> hwscale)); + i2c_w(sd, 0x19, vwsbase + (win->y >> vwscale)); + i2c_w(sd, 0x1a, vwebase + ((win->y + win->height) >> vwscale)); + } + return 0; +} + +static int ov_sensor_mode_setup(struct sd *sd, + int width, int height) +{ + struct ovsensor_window win; + +/* win.format = mode; */ + + /* Unless subcapture is enabled, + * center the image window and downsample + * if possible to increase the field of view */ + /* NOTE: OV518(+) and OV519 does downsampling on its own */ + win.width = width; + win.height = height; + if (width == sd->maxwidth) + win.quarter = 0; + else + win.quarter = 1; + + /* Center it */ + win.x = (win.width - width) / 2; + win.y = (win.height - height) / 2; + + /* Clock is determined by OV519 frame rate code */ + win.clockdiv = sd->clockdiv; + + PDEBUG(D_CONF, "Setting clock divider to %d", win.clockdiv); + return set_ov_sensor_window(sd, &win); +} + +/* -- start the camera -- */ +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + + ret = ov519_mode_init_regs(sd, gspca_dev->width, gspca_dev->height); + if (ret < 0) + goto out; + ret = ov_sensor_mode_setup(sd, gspca_dev->width, gspca_dev->height); + if (ret < 0) + goto out; + + ret = ov51x_restart((struct sd *) gspca_dev); + if (ret < 0) + goto out; + PDEBUG(D_STREAM, "camera started alt: 0x%02x", gspca_dev->alt); + ov51x_led_control(sd, 1); + return; +out: + PDEBUG(D_ERR, "camera start error:%d", ret); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + ov51x_stop((struct sd *) gspca_dev); + ov51x_led_control((struct sd *) gspca_dev, 0); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + unsigned char *data, /* isoc packet */ + int len) /* iso packet length */ +{ + /* Header of ov519 is 16 bytes: + * Byte Value Description + * 0 0xff magic + * 1 0xff magic + * 2 0xff magic + * 3 0xXX 0x50 = SOF, 0x51 = EOF + * 9 0xXX 0x01 initial frame without data, + * 0x00 standard frame with image + * 14 Lo in EOF: length of image data / 8 + * 15 Hi + */ + + if (data[0] == 0xff && data[1] == 0xff && data[2] == 0xff) { + switch (data[3]) { + case 0x50: /* start of frame */ +#define HDRSZ 16 + data += HDRSZ; + len -= HDRSZ; +#undef HDRSZ + if (data[0] == 0xff || data[1] == 0xd8) + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + else + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + case 0x51: /* end of frame */ + if (data[9] != 0) + gspca_dev->last_packet_type = DISCARD_PACKET; + gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, 0); + return; + } + } + + /* intermediate packet */ + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + data, len); +} + +/* -- management routines -- */ + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int val; +/* int was_streaming; */ + + val = sd->brightness; + PDEBUG(D_CONF, "brightness:%d", val); +/* was_streaming = gspca_dev->streaming; + * if (was_streaming) + * ov51x_stop(sd); */ + switch (sd->sensor) { + case SEN_OV8610: + case SEN_OV7610: + case SEN_OV76BE: + case SEN_OV6620: + case SEN_OV6630: + case SEN_OV7640: + i2c_w(sd, OV7610_REG_BRT, val); + break; + case SEN_OV7620: + /* 7620 doesn't like manual changes when in auto mode */ +/*fixme + * if (!sd->auto_brt) */ + i2c_w(sd, OV7610_REG_BRT, val); + break; + case SEN_OV7670: +/*jfm - from windblows + * i2c_w_mask(sd, OV7670_REG_COM8, 0, OV7670_COM8_AEC); */ + i2c_w(sd, OV7670_REG_BRIGHT, ov7670_abs_to_sm(val)); + break; + } +/* if (was_streaming) + * ov51x_restart(sd); */ +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int val; +/* int was_streaming; */ + + val = sd->contrast; + PDEBUG(D_CONF, "contrast:%d", val); +/* was_streaming = gspca_dev->streaming; + if (was_streaming) + ov51x_stop(sd); */ + switch (sd->sensor) { + case SEN_OV7610: + case SEN_OV6620: + i2c_w(sd, OV7610_REG_CNT, val); + break; + case SEN_OV6630: + i2c_w_mask(sd, OV7610_REG_CNT, val >> 4, 0x0f); + case SEN_OV8610: { + static __u8 ctab[] = { + 0x03, 0x09, 0x0b, 0x0f, 0x53, 0x6f, 0x35, 0x7f + }; + + /* Use Y gamma control instead. Bit 0 enables it. */ + i2c_w(sd, 0x64, ctab[val >> 5]); + break; + } + case SEN_OV7620: { + static __u8 ctab[] = { + 0x01, 0x05, 0x09, 0x11, 0x15, 0x35, 0x37, 0x57, + 0x5b, 0xa5, 0xa7, 0xc7, 0xc9, 0xcf, 0xef, 0xff + }; + + /* Use Y gamma control instead. Bit 0 enables it. */ + i2c_w(sd, 0x64, ctab[val >> 4]); + break; + } + case SEN_OV7640: + /* Use gain control instead. */ + i2c_w(sd, OV7610_REG_GAIN, val >> 2); + break; + case SEN_OV7670: + /* check that this isn't just the same as ov7610 */ + i2c_w(sd, OV7670_REG_CONTRAS, val >> 1); + break; + } +/* if (was_streaming) + ov51x_restart(sd); */ +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int val; +/* int was_streaming; */ + + val = sd->colors; + PDEBUG(D_CONF, "saturation:%d", val); +/* was_streaming = gspca_dev->streaming; + if (was_streaming) + ov51x_stop(sd); */ + switch (sd->sensor) { + case SEN_OV8610: + case SEN_OV7610: + case SEN_OV76BE: + case SEN_OV6620: + case SEN_OV6630: + i2c_w(sd, OV7610_REG_SAT, val); + break; + case SEN_OV7620: + /* Use UV gamma control instead. Bits 0 & 7 are reserved. */ +/* rc = ov_i2c_write(sd->dev, 0x62, (val >> 9) & 0x7e); + if (rc < 0) + goto out; */ + i2c_w(sd, OV7610_REG_SAT, val); + break; + case SEN_OV7640: + i2c_w(sd, OV7610_REG_SAT, val & 0xf0); + break; + case SEN_OV7670: + /* supported later once I work out how to do it + * transparently fail now! */ + /* set REG_COM13 values for UV sat auto mode */ + break; + } +/* if (was_streaming) + ov51x_restart(sd); */ +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->colors; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x041e, 0x4052), DVNM("Creative Live! VISTA IM")}, + {USB_DEVICE(0x041e, 0x405f), DVNM("Creative Live! VISTA VF0330")}, + {USB_DEVICE(0x041e, 0x4060), DVNM("Creative Live! VISTA VF0350")}, + {USB_DEVICE(0x041e, 0x4061), DVNM("Creative Live! VISTA VF0400")}, + {USB_DEVICE(0x041e, 0x4064), DVNM("Creative Live! VISTA VF0420")}, + {USB_DEVICE(0x041e, 0x4068), DVNM("Creative Live! VISTA VF0470")}, + {USB_DEVICE(0x045e, 0x028c), DVNM("Microsoft xbox cam")}, + {USB_DEVICE(0x054c, 0x0154), DVNM("Sonny toy4")}, + {USB_DEVICE(0x054c, 0x0155), DVNM("Sonny toy5")}, + {USB_DEVICE(0x05a9, 0x0519), DVNM("OmniVision")}, + {USB_DEVICE(0x05a9, 0x0530), DVNM("OmniVision")}, + {USB_DEVICE(0x05a9, 0x4519), DVNM("OmniVision")}, + {USB_DEVICE(0x05a9, 0x8519), DVNM("OmniVision")}, + {} +}; +#undef DVNAME +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); + +module_param(frame_rate, int, 0644); +MODULE_PARM_DESC(frame_rate, "Frame rate (5, 10, 15, 20 or 30 fps)"); diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c index 482ef4a6afc0..72a5b89cd59d 100644 --- a/drivers/media/video/gspca/pac207.c +++ b/drivers/media/video/gspca/pac207.c @@ -27,8 +27,8 @@ #include "gspca.h" -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(0, 2, 15) -static const char version[] = "0.2.15"; +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; MODULE_AUTHOR("Hans de Goede "); MODULE_DESCRIPTION("Pixart PAC207"); @@ -297,7 +297,6 @@ static int sd_open(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; sd->autogain = 1; - return 0; } @@ -338,7 +337,7 @@ static void sd_start(struct gspca_dev *gspca_dev) pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */ pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */ - udelay(1000); /* taken from gspca */ + msleep(10); pac207_write_reg(gspca_dev, 0x40, 0x01); /* Start ISO pipe */ sd->sof_read = 0; @@ -743,8 +742,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, PDEBUG(D_STREAM, "Incomplete frame"); } pac207_decode_frame_init(gspca_dev); - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, - 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0); len -= sof - data; data = sof; } diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c new file mode 100644 index 000000000000..8f51976db995 --- /dev/null +++ b/drivers/media/video/gspca/pac7311.c @@ -0,0 +1,754 @@ +/* + * Pixart PAC7311 library + * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li + * + * V4L2 by Jean-Francois Moine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "pac7311" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li"); +MODULE_DESCRIPTION("Pixart PAC7311"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + int avg_lum; + + unsigned char brightness; +#define BRIGHTNESS_MAX 0x20 + unsigned char contrast; + unsigned char colors; + unsigned char autogain; + + char ffseq; + signed char ag_cnt; +#define AG_CNT_START 13 +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = BRIGHTNESS_MAX, + .step = 1, + .default_value = 0x10, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define SD_COLOR 2 + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Color", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, +#define SD_AUTOGAIN 3 + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +}; + +static struct cam_mode vga_mode[] = { + {V4L2_PIX_FMT_JPEG, 160, 120, 2}, + {V4L2_PIX_FMT_JPEG, 320, 240, 1}, + {V4L2_PIX_FMT_JPEG, 640, 480, 0}, +}; + +#define PAC7311_JPEG_HEADER_SIZE (sizeof pac7311_jpeg_header) /* (594) */ + +const unsigned char pac7311_jpeg_header[] = { + 0xff, 0xd8, + 0xff, 0xe0, 0x00, 0x03, 0x20, + 0xff, 0xc0, 0x00, 0x11, 0x08, + 0x01, 0xe0, /* 12: height */ + 0x02, 0x80, /* 14: width */ + 0x03, /* 16 */ + 0x01, 0x21, 0x00, + 0x02, 0x11, 0x01, + 0x03, 0x11, 0x01, + 0xff, 0xdb, 0x00, 0x84, + 0x00, 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, 0x0d, + 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, 0x1a, 0x18, 0x16, + 0x16, 0x18, 0x31, 0x23, 0x25, 0x1d, 0x28, 0x3a, 0x33, 0x3d, + 0x3c, 0x39, 0x33, 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, + 0x44, 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, 0x5f, + 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, 0x79, 0x70, 0x64, + 0x78, 0x5c, 0x65, 0x67, 0x63, 0x01, 0x11, 0x12, 0x12, 0x18, + 0x15, 0x18, 0x2f, 0x1a, 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0xff, 0xc4, 0x01, 0xa2, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, + 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, + 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, + 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, + 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, + 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, + 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, + 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, + 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, + 0x0b, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, + 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, + 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, + 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, + 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, + 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, + 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, + 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, + 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, + 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, + 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, + 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, + 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, + 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, + 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, + 0x11, 0x00, 0x3f, 0x00 +}; + +static void reg_w(struct usb_device *dev, + __u16 req, + __u16 value, + __u16 index, + __u8 *buffer, __u16 length) +{ + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, buffer, length, + 500); +} + +static void pac7311_reg_read(struct usb_device *dev, __u16 index, + __u8 *buffer) +{ + usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + 0, /* request */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, buffer, 1, + 500); +} + +static void pac7311_reg_write(struct usb_device *dev, + __u16 index, + __u8 value) +{ + __u8 buf; + + buf = value; + reg_w(dev, 0x00, value, index, &buf, 1); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + struct cam *cam; + + PDEBUG(D_CONF, "Find Sensor PAC7311"); + pac7311_reg_write(dev, 0x78, 0x40); /* Bit_0=start stream, Bit_7=LED */ + pac7311_reg_write(dev, 0x78, 0x40); /* Bit_0=start stream, Bit_7=LED */ + pac7311_reg_write(dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */ + pac7311_reg_write(dev, 0xff, 0x04); + pac7311_reg_write(dev, 0x27, 0x80); + pac7311_reg_write(dev, 0x28, 0xca); + pac7311_reg_write(dev, 0x29, 0x53); + pac7311_reg_write(dev, 0x2a, 0x0e); + pac7311_reg_write(dev, 0xff, 0x01); + pac7311_reg_write(dev, 0x3e, 0x20); + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x05; + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; + sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value; + sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value; + return 0; +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int brightness; + +/*jfm: inverted?*/ + brightness = BRIGHTNESS_MAX - sd->brightness; + pac7311_reg_write(gspca_dev->dev, 0xff, 0x04); + /* pac7311_reg_write(gspca_dev->dev, 0x0e, 0x00); */ + pac7311_reg_write(gspca_dev->dev, 0x0f, brightness); + /* load registers to sensor (Bit 0, auto clear) */ + pac7311_reg_write(gspca_dev->dev, 0x11, 0x01); + PDEBUG(D_CONF|D_STREAM, "brightness: %i", brightness); +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + pac7311_reg_write(gspca_dev->dev, 0xff, 0x01); + pac7311_reg_write(gspca_dev->dev, 0x80, sd->contrast); + /* load registers to sensor (Bit 0, auto clear) */ + pac7311_reg_write(gspca_dev->dev, 0x11, 0x01); + PDEBUG(D_CONF|D_STREAM, "contrast: %i", sd->contrast); +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + pac7311_reg_write(gspca_dev->dev, 0xff, 0x01); + pac7311_reg_write(gspca_dev->dev, 0x10, sd->colors); + /* load registers to sensor (Bit 0, auto clear) */ + pac7311_reg_write(gspca_dev->dev, 0x11, 0x01); + PDEBUG(D_CONF|D_STREAM, "color: %i", sd->colors); +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + pac7311_reg_write(gspca_dev->dev, 0x78, 0x00); /* Turn on LED */ + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + struct sd *sd = (struct sd *) gspca_dev; + + pac7311_reg_write(dev, 0xff, 0x01); + reg_w(dev, 0x01, 0, 0x0002, "\x48\x0a\x40\x08\x00\x00\x08\x00", 8); + reg_w(dev, 0x01, 0, 0x000a, "\x06\xff\x11\xff\x5a\x30\x90\x4c", 8); + reg_w(dev, 0x01, 0, 0x0012, "\x00\x07\x00\x0a\x10\x00\xa0\x10", 8); + reg_w(dev, 0x01, 0, 0x001a, "\x02\x00\x00\x00\x00\x0b\x01\x00", 8); + reg_w(dev, 0x01, 0, 0x0022, "\x00\x00\x00\x00\x00\x00\x00\x00", 8); + reg_w(dev, 0x01, 0, 0x002a, "\x00\x00\x00", 3); + reg_w(dev, 0x01, 0, 0x003e, "\x00\x00\x78\x52\x4a\x52\x78\x6e", 8); + reg_w(dev, 0x01, 0, 0x0046, "\x48\x46\x48\x6e\x5f\x49\x42\x49", 8); + reg_w(dev, 0x01, 0, 0x004e, "\x5f\x5f\x49\x42\x49\x5f\x6e\x48", 8); + reg_w(dev, 0x01, 0, 0x0056, "\x46\x48\x6e\x78\x52\x4a\x52\x78", 8); + reg_w(dev, 0x01, 0, 0x005e, "\x00\x00\x09\x1b\x34\x49\x5c\x9b", 8); + reg_w(dev, 0x01, 0, 0x0066, "\xd0\xff", 2); + reg_w(dev, 0x01, 0, 0x0078, "\x44\x00\xf2\x01\x01\x80", 6); + reg_w(dev, 0x01, 0, 0x007f, "\x2a\x1c\x00\xc8\x02\x58\x03\x84", 8); + reg_w(dev, 0x01, 0, 0x0087, "\x12\x00\x1a\x04\x08\x0c\x10\x14", 8); + reg_w(dev, 0x01, 0, 0x008f, "\x18\x20", 2); + reg_w(dev, 0x01, 0, 0x0096, "\x01\x08\x04", 3); + reg_w(dev, 0x01, 0, 0x00a0, "\x44\x44\x44\x04", 4); + reg_w(dev, 0x01, 0, 0x00f0, "\x01\x00\x00\x00\x22\x00\x20\x00", 8); + reg_w(dev, 0x01, 0, 0x00f8, "\x3f\x00\x0a\x01\x00", 5); + + pac7311_reg_write(dev, 0xff, 0x04); + pac7311_reg_write(dev, 0x02, 0x04); + pac7311_reg_write(dev, 0x03, 0x54); + pac7311_reg_write(dev, 0x04, 0x07); + pac7311_reg_write(dev, 0x05, 0x2b); + pac7311_reg_write(dev, 0x06, 0x09); + pac7311_reg_write(dev, 0x07, 0x0f); + pac7311_reg_write(dev, 0x08, 0x09); + pac7311_reg_write(dev, 0x09, 0x00); + pac7311_reg_write(dev, 0x0c, 0x07); + pac7311_reg_write(dev, 0x0d, 0x00); + pac7311_reg_write(dev, 0x0e, 0x00); + pac7311_reg_write(dev, 0x0f, 0x62); + pac7311_reg_write(dev, 0x10, 0x08); + pac7311_reg_write(dev, 0x12, 0x07); + pac7311_reg_write(dev, 0x13, 0x00); + pac7311_reg_write(dev, 0x14, 0x00); + pac7311_reg_write(dev, 0x15, 0x00); + pac7311_reg_write(dev, 0x16, 0x00); + pac7311_reg_write(dev, 0x17, 0x00); + pac7311_reg_write(dev, 0x18, 0x00); + pac7311_reg_write(dev, 0x19, 0x00); + pac7311_reg_write(dev, 0x1a, 0x00); + pac7311_reg_write(dev, 0x1b, 0x03); + pac7311_reg_write(dev, 0x1c, 0xa0); + pac7311_reg_write(dev, 0x1d, 0x01); + pac7311_reg_write(dev, 0x1e, 0xf4); + pac7311_reg_write(dev, 0x21, 0x00); + pac7311_reg_write(dev, 0x22, 0x08); + pac7311_reg_write(dev, 0x24, 0x03); + pac7311_reg_write(dev, 0x26, 0x00); + pac7311_reg_write(dev, 0x27, 0x01); + pac7311_reg_write(dev, 0x28, 0xca); + pac7311_reg_write(dev, 0x29, 0x10); + pac7311_reg_write(dev, 0x2a, 0x06); + pac7311_reg_write(dev, 0x2b, 0x78); + pac7311_reg_write(dev, 0x2c, 0x00); + pac7311_reg_write(dev, 0x2d, 0x00); + pac7311_reg_write(dev, 0x2e, 0x00); + pac7311_reg_write(dev, 0x2f, 0x00); + pac7311_reg_write(dev, 0x30, 0x23); + pac7311_reg_write(dev, 0x31, 0x28); + pac7311_reg_write(dev, 0x32, 0x04); + pac7311_reg_write(dev, 0x33, 0x11); + pac7311_reg_write(dev, 0x34, 0x00); + pac7311_reg_write(dev, 0x35, 0x00); + pac7311_reg_write(dev, 0x11, 0x01); + setcontrast(gspca_dev); + setbrightness(gspca_dev); + setcolors(gspca_dev); + + /* set correct resolution */ + switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode) { + case 2: /* 160x120 */ + pac7311_reg_write(dev, 0xff, 0x04); + pac7311_reg_write(dev, 0x02, 0x03); + pac7311_reg_write(dev, 0xff, 0x01); + pac7311_reg_write(dev, 0x08, 0x09); + pac7311_reg_write(dev, 0x17, 0x20); + pac7311_reg_write(dev, 0x1b, 0x00); +/* pac7311_reg_write(dev, 0x80, 0x69); */ + pac7311_reg_write(dev, 0x87, 0x10); + break; + case 1: /* 320x240 */ + pac7311_reg_write(dev, 0xff, 0x04); + pac7311_reg_write(dev, 0x02, 0x03); + pac7311_reg_write(dev, 0xff, 0x01); + pac7311_reg_write(dev, 0x08, 0x09); + pac7311_reg_write(dev, 0x17, 0x30); +/* pac7311_reg_write(dev, 0x80, 0x3f); */ + pac7311_reg_write(dev, 0x87, 0x11); + break; + case 0: /* 640x480 */ + pac7311_reg_write(dev, 0xff, 0x04); + pac7311_reg_write(dev, 0x02, 0x03); + pac7311_reg_write(dev, 0xff, 0x01); + pac7311_reg_write(dev, 0x08, 0x08); + pac7311_reg_write(dev, 0x17, 0x00); +/* pac7311_reg_write(dev, 0x80, 0x1c); */ + pac7311_reg_write(dev, 0x87, 0x12); + break; + } + + /* start stream */ + pac7311_reg_write(dev, 0xff, 0x01); + pac7311_reg_write(dev, 0x78, 0x04); + pac7311_reg_write(dev, 0x78, 0x05); + + if (sd->autogain) { + sd->ag_cnt = AG_CNT_START; + sd->avg_lum = 0; + } else { + sd->ag_cnt = -1; + } +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + + pac7311_reg_write(dev, 0xff, 0x04); + pac7311_reg_write(dev, 0x27, 0x80); + pac7311_reg_write(dev, 0x28, 0xca); + pac7311_reg_write(dev, 0x29, 0x53); + pac7311_reg_write(dev, 0x2a, 0x0e); + pac7311_reg_write(dev, 0xff, 0x01); + pac7311_reg_write(dev, 0x3e, 0x20); + pac7311_reg_write(dev, 0x78, 0x04); /* Bit_0=start stream, Bit_7=LED */ + pac7311_reg_write(dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */ + pac7311_reg_write(dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */ +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +/* this function is called at close time */ +static void sd_close(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + + pac7311_reg_write(dev, 0xff, 0x04); + pac7311_reg_write(dev, 0x27, 0x80); + pac7311_reg_write(dev, 0x28, 0xca); + pac7311_reg_write(dev, 0x29, 0x53); + pac7311_reg_write(dev, 0x2a, 0x0e); + pac7311_reg_write(dev, 0xff, 0x01); + pac7311_reg_write(dev, 0x3e, 0x20); + pac7311_reg_write(dev, 0x78, 0x04); /* Bit_0=start stream, Bit_7=LED */ + pac7311_reg_write(dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */ + pac7311_reg_write(dev, 0x78, 0x44); /* Bit_0=start stream, Bit_7=LED */ +} + +static void setautogain(struct gspca_dev *gspca_dev, int luma) +{ + int luma_mean = 128; + int luma_delta = 20; + __u8 spring = 5; + __u8 Pxclk; + int Gbright; + + pac7311_reg_read(gspca_dev->dev, 0x02, &Pxclk); + Gbright = Pxclk; + PDEBUG(D_FRAM, "luma mean %d", luma); + if (luma < luma_mean - luma_delta || + luma > luma_mean + luma_delta) { + Gbright += (luma_mean - luma) >> spring; + if (Gbright > 0x1a) + Gbright = 0x1a; + else if (Gbright < 4) + Gbright = 4; + PDEBUG(D_FRAM, "gbright %d", Gbright); + pac7311_reg_write(gspca_dev->dev, 0xff, 0x04); + pac7311_reg_write(gspca_dev->dev, 0x0f, Gbright); + /* load registers to sensor (Bit 0, auto clear) */ + pac7311_reg_write(gspca_dev->dev, 0x11, 0x01); + } +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + unsigned char *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + unsigned char tmpbuf[4]; + int i, p, ffseq; + +/* if (len < 5) { */ + if (len < 6) { +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + + ffseq = sd->ffseq; + + for (p = 0; p < len - 6; p++) { + if ((data[0 + p] == 0xff) + && (data[1 + p] == 0xff) + && (data[2 + p] == 0x00) + && (data[3 + p] == 0xff) + && (data[4 + p] == 0x96)) { + + /* start of frame */ + if (sd->ag_cnt >= 0 && p > 28) { + sd->avg_lum += data[p - 23]; + if (--sd->ag_cnt < 0) { + sd->ag_cnt = AG_CNT_START; + setautogain(gspca_dev, + sd->avg_lum / AG_CNT_START); + sd->avg_lum = 0; + } + } + + /* copy the end of data to the current frame */ + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, p); + + /* put the JPEG header in the new frame */ + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + (unsigned char *) pac7311_jpeg_header, + 12); + tmpbuf[0] = gspca_dev->height >> 8; + tmpbuf[1] = gspca_dev->height & 0xff; + tmpbuf[2] = gspca_dev->width >> 8; + tmpbuf[3] = gspca_dev->width & 0xff; + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + tmpbuf, 4); + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + (unsigned char *) &pac7311_jpeg_header[16], + PAC7311_JPEG_HEADER_SIZE - 16); + + data += p + 7; + len -= p + 7; + ffseq = 0; + break; + } + } + + /* remove the 'ff ff ff xx' sequences */ + switch (ffseq) { + case 3: + data += 1; + len -= 1; + break; + case 2: + if (data[0] == 0xff) { + data += 2; + len -= 2; + frame->data_end -= 2; + } + break; + case 1: + if (data[0] == 0xff + && data[1] == 0xff) { + data += 3; + len -= 3; + frame->data_end -= 1; + } + break; + } + for (i = 0; i < len - 4; i++) { + if (data[i] == 0xff + && data[i + 1] == 0xff + && data[i + 2] == 0xff) { + memmove(&data[i], &data[i + 4], len - i - 4); + len -= 4; + } + } + ffseq = 0; + if (data[len - 4] == 0xff) { + if (data[len - 3] == 0xff + && data[len - 2] == 0xff) { + len -= 4; + } + } else if (data[len - 3] == 0xff) { + if (data[len - 2] == 0xff + && data[len - 1] == 0xff) + ffseq = 3; + } else if (data[len - 2] == 0xff) { + if (data[len - 1] == 0xff) + ffseq = 2; + } else if (data[len - 1] == 0xff) + ffseq = 1; + sd->ffseq = ffseq; + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); +} + +static void getbrightness(struct gspca_dev *gspca_dev) +{ +/* __u8 brightness = 0; + + pac7311_reg_read(gspca_dev->dev, 0x0008, &brightness); + spca50x->brightness = brightness; + return spca50x->brightness; */ +/* PDEBUG(D_CONF, "Called pac7311_getbrightness: Not implemented yet"); */ +} + + + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + +/* getcontrast(gspca_dev); */ + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + +/* getcolors(gspca_dev); */ + *val = sd->colors; + return 0; +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + if (val) { + sd->ag_cnt = AG_CNT_START; + sd->avg_lum = 0; + } else { + sd->ag_cnt = -1; + } + return 0; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x093a, 0x2600), DVNM("Typhoon")}, + {USB_DEVICE(0x093a, 0x2601), DVNM("Philips SPC610NC")}, + {USB_DEVICE(0x093a, 0x2603), DVNM("PAC7312")}, + {USB_DEVICE(0x093a, 0x2608), DVNM("Trust WB-3300p")}, + {USB_DEVICE(0x093a, 0x260e), DVNM("Gigaware VGA PC Camera, Trust WB-3350p, SIGMA cam 2350")}, + {USB_DEVICE(0x093a, 0x260f), DVNM("SnakeCam")}, + {USB_DEVICE(0x093a, 0x2621), DVNM("PAC731x")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c new file mode 100644 index 000000000000..d26255ddfd5b --- /dev/null +++ b/drivers/media/video/gspca/sonixb.c @@ -0,0 +1,879 @@ +/* + * sonix sn9c102 (bayer) library + * Copyright (C) 2003 2004 Michel Xhaard mxhaard@magic.fr + * Add Pas106 Stefano Mozzi (C) 2004 + * + * V4L2 by Jean-Francois Moine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "sonixb" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/SN9C102 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + unsigned char brightness; + unsigned char contrast; + + char sensor; /* Type of image sensor chip */ +#define SENSOR_HV7131R 0 +#define SENSOR_OV6650 1 +#define SENSOR_OV7630 2 +#define SENSOR_OV7630_3 3 +#define SENSOR_PAS106 4 +#define SENSOR_PAS202 5 +#define SENSOR_TAS5110 6 +#define SENSOR_TAS5130CXX 7 +}; + +#define COMP2 0x8f +#define COMP 0xc7 /* 0x87 //0x07 */ +#define COMP1 0xc9 /* 0x89 //0x09 */ + +#define MCK_INIT 0x63 +#define MCK_INIT1 0x20 /*fixme: Bayer - 0x50 for JPEG ??*/ + +#define SYS_CLK 0x04 + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +}; + +/* fixme: should have V4L2_PIX_FMT_SN9C10X */ +static struct cam_mode vga_mode[] = { + {V4L2_PIX_FMT_SN9C10X, 160, 120, 2}, + {V4L2_PIX_FMT_SN9C10X, 320, 240, 1}, + {V4L2_PIX_FMT_SN9C10X, 640, 480, 0}, +}; +static struct cam_mode sif_mode[] = { + {V4L2_PIX_FMT_SN9C10X, 176, 144, 1}, + {V4L2_PIX_FMT_SN9C10X, 352, 288, 0}, +}; + +static const __u8 probe_ov7630[] = {0x08, 0x44}; + +static const __u8 initHv7131[] = { + 0x46, 0x77, 0x00, 0x04, 0x00, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, /* shift from 0x02 0x01 0x00 */ + 0x28, 0x1e, 0x60, 0x8a, 0x20, + 0x1d, 0x10, 0x02, 0x03, 0x0f, 0x0c +}; +static const __u8 hv7131_sensor_init[][8] = { + {0xc0, 0x11, 0x31, 0x38, 0x2a, 0x2e, 0x00, 0x10}, + {0xa0, 0x11, 0x01, 0x08, 0x2a, 0x2e, 0x00, 0x10}, + {0xb0, 0x11, 0x20, 0x00, 0xd0, 0x2e, 0x00, 0x10}, + {0xc0, 0x11, 0x25, 0x03, 0x0e, 0x28, 0x00, 0x16}, + {0xa0, 0x11, 0x30, 0x10, 0x0e, 0x28, 0x00, 0x15}, +}; +static const __u8 initOv6650[] = { + 0x44, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x01, 0x0a, 0x16, 0x12, 0x68, 0x0b, + 0x10, 0x1d, 0x10, 0x00, 0x06, 0x1f, 0x00 +}; +static const __u8 ov6650_sensor_init[][8] = +{ + /* Bright, contrast, etc are set througth SCBB interface. + * AVCAP on win2 do not send any data on this controls. */ + /* Anyway, some registers appears to alter bright and constrat */ + {0xa0, 0x60, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xd0, 0x60, 0x11, 0xc0, 0x1b, 0x18, 0xc1, 0x10}, + {0xb0, 0x60, 0x15, 0x00, 0x02, 0x18, 0xc1, 0x10}, +/* {0xa0, 0x60, 0x1b, 0x01, 0x02, 0x18, 0xc1, 0x10}, + * THIS SET GREEN SCREEN + * (pixels could be innverted in decode kind of "brg", + * but blue wont be there. Avoid this data ... */ + {0xd0, 0x60, 0x26, 0x01, 0x14, 0xd8, 0xa4, 0x10}, /* format out? */ + {0xd0, 0x60, 0x26, 0x01, 0x14, 0xd8, 0xa4, 0x10}, + {0xa0, 0x60, 0x30, 0x3d, 0x0A, 0xd8, 0xa4, 0x10}, + {0xb0, 0x60, 0x60, 0x66, 0x68, 0xd8, 0xa4, 0x10}, + {0xa0, 0x60, 0x68, 0x04, 0x68, 0xd8, 0xa4, 0x10}, + {0xd0, 0x60, 0x17, 0x24, 0xd6, 0x04, 0x94, 0x10}, /* Clipreg */ + {0xa0, 0x60, 0x10, 0x5d, 0x99, 0x04, 0x94, 0x16}, + {0xa0, 0x60, 0x2d, 0x0a, 0x99, 0x04, 0x94, 0x16}, + {0xa0, 0x60, 0x32, 0x00, 0x99, 0x04, 0x94, 0x16}, + {0xa0, 0x60, 0x33, 0x40, 0x99, 0x04, 0x94, 0x16}, + {0xa0, 0x60, 0x11, 0xc0, 0x99, 0x04, 0x94, 0x16}, + {0xa0, 0x60, 0x00, 0x16, 0x99, 0x04, 0x94, 0x15}, /* bright / Lumino */ + {0xa0, 0x60, 0x2b, 0xab, 0x99, 0x04, 0x94, 0x15}, + /* ?flicker o brillo */ + {0xa0, 0x60, 0x2d, 0x2a, 0x99, 0x04, 0x94, 0x15}, + {0xa0, 0x60, 0x2d, 0x2b, 0x99, 0x04, 0x94, 0x16}, + {0xa0, 0x60, 0x32, 0x00, 0x99, 0x04, 0x94, 0x16}, + {0xa0, 0x60, 0x33, 0x00, 0x99, 0x04, 0x94, 0x16}, + {0xa0, 0x60, 0x10, 0x57, 0x99, 0x04, 0x94, 0x16}, + {0xa0, 0x60, 0x2d, 0x2b, 0x99, 0x04, 0x94, 0x16}, + {0xa0, 0x60, 0x32, 0x00, 0x99, 0x04, 0x94, 0x16}, + /* Low Light (Enabled: 0x32 0x1 | Disabled: 0x32 0x00) */ + {0xa0, 0x60, 0x33, 0x29, 0x99, 0x04, 0x94, 0x16}, + /* Low Ligth (Enabled: 0x33 0x13 | Disabled: 0x33 0x29) */ +/* {0xa0, 0x60, 0x11, 0xc1, 0x99, 0x04, 0x94, 0x16}, */ + {0xa0, 0x60, 0x00, 0x17, 0x99, 0x04, 0x94, 0x15}, /* clip? r */ + {0xa0, 0x60, 0x00, 0x18, 0x99, 0x04, 0x94, 0x15}, /* clip? r */ +}; +static const __u8 initOv7630[] = { + 0x04, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, /* r01 .. r08 */ + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* r09 .. r10 */ + 0x00, 0x02, 0x01, 0x0a, /* r11 .. r14 */ + 0x28, 0x1e, /* H & V sizes r15 .. r16 */ + 0x68, COMP1, MCK_INIT1, /* r17 .. r19 */ + 0x1d, 0x10, 0x02, 0x03, 0x0f, 0x0c /* r1a .. r1f */ +}; +static const __u8 initOv7630_3[] = { + 0x44, 0x44, 0x00, 0x1a, 0x20, 0x20, 0x20, 0x80, /* r01 .. r08 */ + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, /* r09 .. r10 */ + 0x00, 0x02, 0x01, 0x0a, /* r11 .. r14 */ + 0x28, 0x1e, /* H & V sizes r15 .. r16 */ + 0x68, COMP1, MCK_INIT1, /* r17 .. r19 */ + 0x1d, 0x10, 0x02, 0x03, 0x0f, 0x0c /* r1a .. r1f */ +}; +static const __u8 ov7630_sensor_init_com[][8] = { + {0xa0, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xb0, 0x21, 0x01, 0x77, 0x3a, 0x00, 0x00, 0x10}, +/* {0xd0, 0x21, 0x12, 0x7c, 0x01, 0x80, 0x34, 0x10}, jfm */ + {0xd0, 0x21, 0x12, 0x78, 0x00, 0x80, 0x34, 0x10}, /* jfm */ + {0xa0, 0x21, 0x1b, 0x04, 0x00, 0x80, 0x34, 0x10}, + {0xa0, 0x21, 0x20, 0x44, 0x00, 0x80, 0x34, 0x10}, + {0xa0, 0x21, 0x23, 0xee, 0x00, 0x80, 0x34, 0x10}, + {0xd0, 0x21, 0x26, 0xa0, 0x9a, 0xa0, 0x30, 0x10}, + {0xb0, 0x21, 0x2a, 0x80, 0x00, 0xa0, 0x30, 0x10}, + {0xb0, 0x21, 0x2f, 0x3d, 0x24, 0xa0, 0x30, 0x10}, + {0xa0, 0x21, 0x32, 0x86, 0x24, 0xa0, 0x30, 0x10}, +/* {0xb0, 0x21, 0x60, 0xa9, 0x4a, 0xa0, 0x30, 0x10}, jfm */ + {0xb0, 0x21, 0x60, 0xa9, 0x42, 0xa0, 0x30, 0x10}, /* jfm */ + {0xa0, 0x21, 0x65, 0x00, 0x42, 0xa0, 0x30, 0x10}, + {0xa0, 0x21, 0x69, 0x38, 0x42, 0xa0, 0x30, 0x10}, + {0xc0, 0x21, 0x6f, 0x88, 0x0b, 0x00, 0x30, 0x10}, + {0xc0, 0x21, 0x74, 0x21, 0x8e, 0x00, 0x30, 0x10}, + {0xa0, 0x21, 0x7d, 0xf7, 0x8e, 0x00, 0x30, 0x10}, + {0xd0, 0x21, 0x17, 0x1c, 0xbd, 0x06, 0xf6, 0x10}, +}; +static const __u8 ov7630_sensor_init[][8] = { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 200ms */ + {0xa0, 0x21, 0x11, 0x01, 0xbd, 0x06, 0xf6, 0x10}, /* jfm */ + {0xa0, 0x21, 0x10, 0x57, 0xbd, 0x06, 0xf6, 0x16}, + {0xa0, 0x21, 0x76, 0x02, 0xbd, 0x06, 0xf6, 0x16}, + {0xa0, 0x21, 0x00, 0x10, 0xbd, 0x06, 0xf6, 0x15}, /* gain */ +}; +static const __u8 ov7630_sensor_init_3[][8] = { + {0xa0, 0x21, 0x10, 0x36, 0xbd, 0x06, 0xf6, 0x16}, /* exposure */ + {0xa0, 0x21, 0x76, 0x03, 0xbd, 0x06, 0xf6, 0x16}, + {0xa0, 0x21, 0x11, 0x01, 0xbd, 0x06, 0xf6, 0x16}, + {0xa0, 0x21, 0x00, 0x10, 0xbd, 0x06, 0xf6, 0x15}, /* gain */ +/* {0xb0, 0x21, 0x2a, 0xc0, 0x3c, 0x06, 0xf6, 0x1d}, + * a0 1c,a0 1f,c0 3c frame rate ?line interval from ov6630 */ +/* {0xb0, 0x21, 0x2a, 0xa0, 0x1f, 0x06, 0xf6, 0x1d}, * from win */ + {0xb0, 0x21, 0x2a, 0xa0, 0x1c, 0x06, 0xf6, 0x1d}, +}; + +static const __u8 initPas106[] = { + 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x01, 0x00, + 0x16, 0x12, 0x28, COMP1, MCK_INIT1, + 0x18, 0x10, 0x04, 0x03, 0x11, 0x0c +}; +/* compression 0x86 mckinit1 0x2b */ +static const __u8 pas106_data[][2] = { + {0x02, 0x04}, /* Pixel Clock Divider 6 */ + {0x03, 0x13}, /* Frame Time MSB */ +/* {0x03, 0x12}, * Frame Time MSB */ + {0x04, 0x06}, /* Frame Time LSB */ +/* {0x04, 0x05}, * Frame Time LSB */ + {0x05, 0x65}, /* Shutter Time Line Offset */ +/* {0x05, 0x6d}, * Shutter Time Line Offset */ +/* {0x06, 0xb1}, * Shutter Time Pixel Offset */ + {0x06, 0xcd}, /* Shutter Time Pixel Offset */ + {0x07, 0xc1}, /* Black Level Subtract Sign */ +/* {0x07, 0x00}, * Black Level Subtract Sign */ + {0x08, 0x06}, /* Black Level Subtract Level */ + {0x08, 0x06}, /* Black Level Subtract Level */ +/* {0x08, 0x01}, * Black Level Subtract Level */ + {0x09, 0x05}, /* Color Gain B Pixel 5 a */ + {0x0a, 0x04}, /* Color Gain G1 Pixel 1 5 */ + {0x0b, 0x04}, /* Color Gain G2 Pixel 1 0 5 */ + {0x0c, 0x05}, /* Color Gain R Pixel 3 1 */ + {0x0d, 0x00}, /* Color GainH Pixel */ + {0x0e, 0x0e}, /* Global Gain */ + {0x0f, 0x00}, /* Contrast */ + {0x10, 0x06}, /* H&V synchro polarity */ + {0x11, 0x06}, /* ?default */ + {0x12, 0x06}, /* DAC scale */ + {0x14, 0x02}, /* ?default */ + {0x13, 0x01}, /* Validate Settings */ +}; +static const __u8 initPas202[] = { + 0x44, 0x44, 0x21, 0x30, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x03, 0x0a, /* 6 */ + 0x28, 0x1e, 0x28, 0x89, 0x30, + 0x00, 0x00, 0x02, 0x03, 0x0f, 0x0c +}; +static const __u8 pas202_sensor_init[][8] = { + {0xa0, 0x40, 0x02, 0x03, 0x00, 0x00, 0x00, 0x10}, + {0xd0, 0x40, 0x04, 0x07, 0x34, 0x00, 0x09, 0x10}, + {0xd0, 0x40, 0x08, 0x01, 0x00, 0x00, 0x01, 0x10}, + {0xd0, 0x40, 0x0C, 0x00, 0x0C, 0x00, 0x32, 0x10}, + {0xd0, 0x40, 0x10, 0x00, 0x01, 0x00, 0x63, 0x10}, + {0xa0, 0x40, 0x15, 0x70, 0x01, 0x00, 0x63, 0x10}, + {0xa0, 0x40, 0x18, 0x00, 0x01, 0x00, 0x63, 0x10}, + {0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10}, + {0xa0, 0x40, 0x03, 0x56, 0x01, 0x00, 0x63, 0x10}, + {0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10}, + {0xb0, 0x40, 0x04, 0x07, 0x2a, 0x00, 0x63, 0x10}, + {0xb0, 0x40, 0x0e, 0x00, 0x3d, 0x00, 0x63, 0x10}, + + {0xa0, 0x40, 0x11, 0x01, 0x3d, 0x00, 0x63, 0x16}, + {0xa0, 0x40, 0x10, 0x08, 0x3d, 0x00, 0x63, 0x15}, + {0xa0, 0x40, 0x02, 0x04, 0x3d, 0x00, 0x63, 0x16}, + {0xa0, 0x40, 0x11, 0x01, 0x3d, 0x00, 0x63, 0x16}, + {0xb0, 0x40, 0x0e, 0x00, 0x31, 0x00, 0x63, 0x16}, + {0xa0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16}, + {0xa0, 0x40, 0x10, 0x0e, 0x31, 0x00, 0x63, 0x15}, + {0xa0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16}, +}; + +static const __u8 initTas5110[] = { + 0x44, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x01, 0x00, 0x46, 0x09, 0x0a, /* shift from 0x45 0x09 0x0a */ + 0x16, 0x12, 0x60, 0x86, 0x2b, + 0x14, 0x0a, 0x02, 0x02, 0x09, 0x07 +}; +static const __u8 tas5110_sensor_init[][8] = { + {0x30, 0x11, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x10}, + {0x30, 0x11, 0x02, 0x20, 0xa9, 0x00, 0x00, 0x10}, + {0xa0, 0x61, 0x9a, 0xca, 0x00, 0x00, 0x00, 0x17}, +}; + +static const __u8 initTas5130[] = { + 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x01, 0x00, 0x69, 0x0c, 0x0a, + 0x28, 0x1e, 0x60, COMP, MCK_INIT, + 0x18, 0x10, 0x04, 0x03, 0x11, 0x0c +}; +static const __u8 tas5130_sensor_init[][8] = { +/* {0x30, 0x11, 0x00, 0x40, 0x47, 0x00, 0x00, 0x10}, + * shutter 0x47 short exposure? */ + {0x30, 0x11, 0x00, 0x40, 0x01, 0x00, 0x00, 0x10}, + /* shutter 0x01 long exposure */ + {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10}, +}; + +static void reg_r(struct usb_device *dev, + __u16 value, __u8 *buffer) +{ + usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + 0, /* request */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, + 0, /* index */ + buffer, 1, + 500); +} + +static void reg_w(struct usb_device *dev, + __u16 value, + const __u8 *buffer, + __u16 len) +{ + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + 0x08, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, + 0, /* index */ + (__u8 *) buffer, len, + 500); +} + +static int i2c_w(struct usb_device *dev, const __u8 *buffer) +{ + int retry = 60; + __u8 ByteReceive; + + /* is i2c ready */ + reg_w(dev, 0x08, buffer, 8); + while (retry--) { + msleep(10); + reg_r(dev, 0x08, &ByteReceive); + if (ByteReceive == 4) + return 0; + } + return -1; +} + +static void i2c_w_vector(struct usb_device *dev, + const __u8 buffer[][8], int len) +{ + for (;;) { + reg_w(dev, 0x08, *buffer, 8); + len -= 8; + if (len <= 0) + break; + buffer++; + } +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 value; + + switch (sd->sensor) { + case SENSOR_OV6650: { + __u8 i2cOV6650[] = + {0xa0, 0x60, 0x06, 0x11, 0x99, 0x04, 0x94, 0x15}; + + i2cOV6650[3] = sd->brightness; + if (i2c_w(gspca_dev->dev, i2cOV6650) < 0) + goto err; + break; + } + case SENSOR_OV7630: { + __u8 i2cOV[] = + {0xa0, 0x21, 0x06, 0x36, 0xbd, 0x06, 0xf6, 0x16}; + + /* change reg 0x06 */ + i2cOV[3] = sd->brightness; + if (i2c_w(gspca_dev->dev, i2cOV) < 0) + goto err; + break; + } + case SENSOR_PAS106: { + __u8 i2c1[] = + {0xa1, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14}; + + i2c1[3] = sd->brightness >> 3; + i2c1[2] = 0x0e; + if (i2c_w(gspca_dev->dev, i2c1) < 0) + goto err; + i2c1[3] = 0x01; + i2c1[2] = 0x13; + if (i2c_w(gspca_dev->dev, i2c1) < 0) + goto err; + break; + } + case SENSOR_PAS202: { + /* __u8 i2cpexpo1[] = + {0xb0, 0x40, 0x04, 0x07, 0x2a, 0x00, 0x63, 0x16}; */ + __u8 i2cpexpo[] = + {0xb0, 0x40, 0x0e, 0x01, 0xab, 0x00, 0x63, 0x16}; + __u8 i2cp202[] = + {0xa0, 0x40, 0x10, 0x0e, 0x31, 0x00, 0x63, 0x15}; + static __u8 i2cpdoit[] = + {0xa0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16}; + + /* change reg 0x10 */ + i2cpexpo[4] = 0xff - sd->brightness; +/* if(i2c_w(gspca_dev->dev,i2cpexpo1) < 0) + goto err; */ +/* if(i2c_w(gspca_dev->dev,i2cpdoit) < 0) + goto err; */ + if (i2c_w(gspca_dev->dev, i2cpexpo) < 0) + goto err; + if (i2c_w(gspca_dev->dev, i2cpdoit) < 0) + goto err; + i2cp202[3] = sd->brightness >> 3; + if (i2c_w(gspca_dev->dev, i2cp202) < 0) + goto err; + if (i2c_w(gspca_dev->dev, i2cpdoit) < 0) + goto err; + break; + } + case SENSOR_TAS5130CXX: + case SENSOR_TAS5110: { + __u8 i2c[] = + {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10}; + + value = 0xff - sd->brightness; + i2c[4] = value; + PDEBUG(D_CONF, "brightness %d : %d", value, i2c[4]); + if (i2c_w(gspca_dev->dev, i2c) < 0) + goto err; + break; + } + } + return; +err: + PDEBUG(D_ERR, "i2c error brightness"); +} +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 gain; + __u8 rgb_value; + + gain = sd->contrast >> 4; + /* red and blue gain */ + rgb_value = gain << 4 | gain; + reg_w(gspca_dev->dev, 0x10, &rgb_value, 1); + /* green gain */ + rgb_value = gain; + reg_w(gspca_dev->dev, 0x11, &rgb_value, 1); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; +/* __u16 vendor; */ + __u16 product; + int sif = 0; + +/* vendor = id->idVendor; */ + product = id->idProduct; +/* switch (vendor) { */ +/* case 0x0c45: * Sonix */ + switch (product) { + case 0x6001: /* SN9C102 */ + case 0x6005: /* SN9C101 */ + case 0x6007: /* SN9C101 */ + sd->sensor = SENSOR_TAS5110; + sif = 1; + break; + case 0x6009: /* SN9C101 */ + case 0x600d: /* SN9C101 */ + case 0x6029: /* SN9C101 */ + sd->sensor = SENSOR_PAS106; + sif = 1; + break; + case 0x6011: /* SN9C101 - SN9C101G */ + sd->sensor = SENSOR_OV6650; + sif = 1; + break; + case 0x6019: /* SN9C101 */ + case 0x602c: /* SN9C102 */ + case 0x602e: /* SN9C102 */ + sd->sensor = SENSOR_OV7630; + break; + case 0x60b0: /* SN9C103 */ + sd->sensor = SENSOR_OV7630_3; + break; + case 0x6024: /* SN9C102 */ + case 0x6025: /* SN9C102 */ + sd->sensor = SENSOR_TAS5130CXX; + break; + case 0x6028: /* SN9C102 */ + sd->sensor = SENSOR_PAS202; + break; + case 0x602d: /* SN9C102 */ + sd->sensor = SENSOR_HV7131R; + break; + case 0x60af: /* SN9C103 */ + sd->sensor = SENSOR_PAS202; + break; + } +/* break; */ +/* } */ + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + if (!sif) { + cam->cam_mode = vga_mode; + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + } else { + cam->cam_mode = sif_mode; + cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; + } + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; + if (sd->sensor == SENSOR_OV7630_3) /* jfm: from win trace */ + reg_w(gspca_dev->dev, 0x01, probe_ov7630, sizeof probe_ov7630); + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + __u8 ByteReceive; + + reg_r(gspca_dev->dev, 0x00, &ByteReceive); + if (ByteReceive != 0x10) + return -ENODEV; + return 0; +} + +static void pas106_i2cinit(struct usb_device *dev) +{ + int i; + const __u8 *data; + __u8 i2c1[] = { 0xa1, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14 }; + + i = ARRAY_SIZE(pas106_data); + data = pas106_data[0]; + while (--i >= 0) { + memcpy(&i2c1[2], data, 2); + /* copy 2 bytes from the template */ + if (i2c_w(dev, i2c1) < 0) + PDEBUG(D_ERR, "i2c error pas106"); + data += 2; + } +} + +/* -- start the camera -- */ +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + int mode, l; + const __u8 *sn9c10x; + __u8 reg01, reg17; + __u8 reg17_19[3]; + + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode; + switch (sd->sensor) { + case SENSOR_HV7131R: + sn9c10x = initHv7131; + reg17_19[0] = 0x60; + reg17_19[1] = (mode << 4) | 0x8a; + reg17_19[2] = 0x20; + break; + case SENSOR_OV6650: + sn9c10x = initOv6650; + reg17_19[0] = 0x68; + reg17_19[1] = (mode << 4) | 0x8b; + reg17_19[2] = 0x20; + break; + case SENSOR_OV7630: + sn9c10x = initOv7630; + reg17_19[0] = 0x68; + reg17_19[1] = (mode << 4) | COMP2; + reg17_19[2] = MCK_INIT1; + break; + case SENSOR_OV7630_3: + sn9c10x = initOv7630_3; + reg17_19[0] = 0x68; + reg17_19[1] = (mode << 4) | COMP2; + reg17_19[2] = MCK_INIT1; + break; + case SENSOR_PAS106: + sn9c10x = initPas106; + reg17_19[0] = 0x24; /* 0x28 */ + reg17_19[1] = (mode << 4) | COMP1; + reg17_19[2] = MCK_INIT1; + break; + case SENSOR_PAS202: + sn9c10x = initPas202; + reg17_19[0] = mode ? 0x24 : 0x20; + reg17_19[1] = (mode << 4) | 0x89; + reg17_19[2] = 0x20; + break; + case SENSOR_TAS5110: + sn9c10x = initTas5110; + reg17_19[0] = 0x60; + reg17_19[1] = (mode << 4) | 0x86; + reg17_19[2] = 0x2b; /* 0xf3; */ + break; + default: +/* case SENSOR_TAS5130CXX: */ + sn9c10x = initTas5130; + reg17_19[0] = 0x60; + reg17_19[1] = (mode << 4) | COMP; + reg17_19[2] = mode ? 0x23 : 0x43; + break; + } + switch (sd->sensor) { + case SENSOR_OV7630: + reg01 = 0x06; + reg17 = 0x29; + l = 0x10; + break; + case SENSOR_OV7630_3: + reg01 = 0x44; + reg17 = 0x68; + l = 0x10; + break; + default: + reg01 = sn9c10x[0]; + reg17 = sn9c10x[0x17 - 1]; + l = 0x1f; + break; + } + + /* reg 0x01 bit 2 video transfert on */ + reg_w(dev, 0x01, ®01, 1); + /* reg 0x17 SensorClk enable inv Clk 0x60 */ + reg_w(dev, 0x17, ®17, 1); +/*fixme: for ov7630 102 + reg_w(dev, 0x01, {0x06, sn9c10x[1]}, 2); */ + /* Set the registers from the template */ + reg_w(dev, 0x01, sn9c10x, l); + switch (sd->sensor) { + case SENSOR_HV7131R: + i2c_w_vector(dev, hv7131_sensor_init, + sizeof hv7131_sensor_init); + break; + case SENSOR_OV6650: + i2c_w_vector(dev, ov6650_sensor_init, + sizeof ov6650_sensor_init); + break; + case SENSOR_OV7630: + i2c_w_vector(dev, ov7630_sensor_init_com, + sizeof ov7630_sensor_init_com); + msleep(200); + i2c_w_vector(dev, ov7630_sensor_init, + sizeof ov7630_sensor_init); + break; + case SENSOR_OV7630_3: + i2c_w_vector(dev, ov7630_sensor_init_com, + sizeof ov7630_sensor_init_com); + msleep(200); + i2c_w_vector(dev, ov7630_sensor_init_3, + sizeof ov7630_sensor_init_3); + break; + case SENSOR_PAS106: + pas106_i2cinit(dev); + break; + case SENSOR_PAS202: + i2c_w_vector(dev, pas202_sensor_init, + sizeof pas202_sensor_init); + break; + case SENSOR_TAS5110: + i2c_w_vector(dev, tas5110_sensor_init, + sizeof tas5110_sensor_init); + break; + default: +/* case SENSOR_TAS5130CXX: */ + i2c_w_vector(dev, tas5130_sensor_init, + sizeof tas5130_sensor_init); + break; + } + /* H_size V_size 0x28, 0x1e maybe 640x480 */ + reg_w(dev, 0x15, &sn9c10x[0x15 - 1], 2); + /* compression register */ + reg_w(dev, 0x18, ®17_19[1], 1); + /* H_start */ /*fixme: not ov7630*/ + reg_w(dev, 0x12, &sn9c10x[0x12 - 1], 1); + /* V_START */ /*fixme: not ov7630*/ + reg_w(dev, 0x13, &sn9c10x[0x13 - 1], 1); + /* reset 0x17 SensorClk enable inv Clk 0x60 */ + /*fixme: ov7630 [17]=68 8f (+20 if 102)*/ + reg_w(dev, 0x17, ®17_19[0], 1); + /*MCKSIZE ->3 */ /*fixme: not ov7630*/ + reg_w(dev, 0x19, ®17_19[2], 1); + /* AE_STRX AE_STRY AE_ENDX AE_ENDY */ + reg_w(dev, 0x1c, &sn9c10x[0x1c - 1], 4); + /* Enable video transfert */ + reg_w(dev, 0x01, &sn9c10x[0], 1); + /* Compression */ + reg_w(dev, 0x18, ®17_19[1], 2); + msleep(20); + + setcontrast(gspca_dev); + setbrightness(gspca_dev); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + __u8 ByteSend = 0; + + ByteSend = 0x09; /* 0X00 */ + reg_w(gspca_dev->dev, 0x01, &ByteSend, 1); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + unsigned char *data, /* isoc packet */ + int len) /* iso packet length */ +{ + int p; + + if (len > 6 && len < 24) { + for (p = 0; p < len - 6; p++) { + if (data[0 + p] == 0xff + && data[1 + p] == 0xff + && data[2 + p] == 0x00 + && data[3 + p] == 0xc4 + && data[4 + p] == 0xc4 + && data[5 + p] == 0x96) { /* start of frame */ + frame = gspca_frame_add(gspca_dev, + LAST_PACKET, + frame, + data, 0); + data += 12; + len -= 12; + gspca_frame_add(gspca_dev, FIRST_PACKET, + frame, data, len); + return; + } + } + } + gspca_frame_add(gspca_dev, INTER_PACKET, + frame, data, len); +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->contrast; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x0c45, 0x6001), DVNM("Genius VideoCAM NB")}, + {USB_DEVICE(0x0c45, 0x6005), DVNM("Sweex Tas5110")}, + {USB_DEVICE(0x0c45, 0x6007), DVNM("Sonix sn9c101 + Tas5110D")}, + {USB_DEVICE(0x0c45, 0x6009), DVNM("spcaCam@120")}, + {USB_DEVICE(0x0c45, 0x600d), DVNM("spcaCam@120")}, + {USB_DEVICE(0x0c45, 0x6011), DVNM("MAX Webcam Microdia-OV6650-SN9C101G")}, + {USB_DEVICE(0x0c45, 0x6019), DVNM("Generic Sonix OV7630")}, + {USB_DEVICE(0x0c45, 0x6024), DVNM("Generic Sonix Tas5130c")}, + {USB_DEVICE(0x0c45, 0x6025), DVNM("Xcam Shanga")}, + {USB_DEVICE(0x0c45, 0x6028), DVNM("Sonix Btc Pc380")}, + {USB_DEVICE(0x0c45, 0x6029), DVNM("spcaCam@150")}, + {USB_DEVICE(0x0c45, 0x602c), DVNM("Generic Sonix OV7630")}, + {USB_DEVICE(0x0c45, 0x602d), DVNM("LIC-200 LG")}, + {USB_DEVICE(0x0c45, 0x602e), DVNM("Genius VideoCam Messenger")}, + {USB_DEVICE(0x0c45, 0x60af), DVNM("Trust WB3100P")}, + {USB_DEVICE(0x0c45, 0x60b0), DVNM("Genius VideoCam Look")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c new file mode 100644 index 000000000000..6180bc565ca1 --- /dev/null +++ b/drivers/media/video/gspca/sonixj.c @@ -0,0 +1,1629 @@ +/* + * Sonix sn9c102p sn9c105 sn9c120 (jpeg) library + * Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr + * + * V4L2 by Jean-Francois Moine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "sonixj" + +#include "gspca.h" +#include "jpeg.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/SONIX JPEG USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + int avg_lum; + unsigned int exposure; + + unsigned short brightness; + unsigned char contrast; + unsigned char colors; + unsigned char autogain; + + signed char ag_cnt; +#define AG_CNT_START 13 + + char qindex; + char sensor; /* Type of image sensor chip */ +#define SENSOR_HV7131R 0 +#define SENSOR_MI0360 1 +#define SENSOR_MO4000 2 +#define SENSOR_OV7648 3 +#define SENSOR_OV7660 4 + unsigned char customid; +#define SN9C102P 0 +#define SN9C105 1 +#define SN9C110 2 +#define SN9C120 3 +#define SN9C325 4 + unsigned char i2c_base; + unsigned char i2c_ctrl_reg; +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 0xffff, + .step = 1, + .default_value = 0x7fff, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 63, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define SD_COLOR 2 + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Color", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, +#define SD_AUTOGAIN 3 + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +}; + +static struct cam_mode vga_mode[] = { + {V4L2_PIX_FMT_JPEG, 160, 120, 2}, + {V4L2_PIX_FMT_JPEG, 320, 240, 1}, + {V4L2_PIX_FMT_JPEG, 640, 480, 0}, +}; + +/*Data from sn9c102p+hv71331r */ +static __u8 sn_hv7131[] = { + 0x00, 0x03, 0x64, 0x00, 0x1A, 0x20, 0x20, 0x20, 0xA1, 0x11, +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 reg9 */ + 0x02, 0x09, 0x00, 0x00, 0x00, 0x10, 0x03, 0x00, /* 00 */ +/* rega regb regc regd rege regf reg10 reg11 */ + 0x00, 0x01, 0x03, 0x28, 0x1e, 0x41, 0x0a, 0x00, 0x00, 0x00, +/* reg12 reg13 reg14 reg15 reg16 reg17 reg18 reg19 reg1a reg1b */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +/* reg1c reg1d reg1e reg1f reg20 reg21 reg22 reg23 */ +}; + +static __u8 sn_mi0360[] = { + 0x00, 0x61, 0x44, 0x00, 0x1a, 0x20, 0x20, 0x20, 0xb1, 0x5d, +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 reg9 */ + 0x07, 0x00, 0x00, 0x00, 0x00, 0x10, 0x03, 0x00, +/* rega regb regc regd rege regf reg10 reg11 */ + 0x00, 0x02, 0x0a, 0x28, 0x1e, 0x61, 0x06, 0x00, 0x00, 0x00, +/* reg12 reg13 reg14 reg15 reg16 reg17 reg18 reg19 reg1a reg1b */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +/* reg1c reg1d reg1e reg1f reg20 reg21 reg22 reg23 */ +}; + +static __u8 sn_mo4000[] = { + 0x12, 0x23, 0x60, 0x00, 0x1A, 0x00, 0x20, 0x18, 0x81, +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 */ + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, +/* reg9 rega regb regc regd rege regf reg10 reg11*/ + 0x0b, 0x0f, 0x14, 0x28, 0x1e, 0x40, 0x08, 0x00, 0x00, +/* reg12 reg13 reg14 reg15 reg16 reg17 reg18 reg19 reg1a*/ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x25, 0x39, 0x4b, +/* reg1b reg1c reg1d reg1e reg1f reg20 reg21 reg22 reg23*/ + 0x5c, 0x6b, 0x79, 0x87, 0x95, 0xa2, 0xaf, 0xbb, 0xc7, + 0xd3, 0xdf, 0xea, 0xf5 +}; + +static __u8 sn_ov7648[] = { + 0x00, 0x21, 0x62, 0x00, 0x1a, 0x20, 0x20, 0x20, 0xA1, 0x6E, 0x18, 0x65, + 0x00, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x06, 0x06, 0x28, 0x1E, 0x82, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static __u8 sn_ov7660[] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 */ + 0x00, 0x61, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x81, +/* reg9 rega regb regc regd rege regf reg10 reg11*/ + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, +/* reg12 reg13 reg14 reg15 reg16 reg17 reg18 reg19 reg1a*/ + 0x01, 0x01, 0x08, 0x28, 0x1e, 0x20, 0x07, 0x00, 0x00, +/* reg1b reg1c reg1d reg1e reg1f reg20 reg21 reg22 reg23*/ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* sequence specific to the sensors - !! index = SENSOR_xxx */ +static __u8 *sn_tb[] = { + sn_hv7131, + sn_mi0360, + sn_mo4000, + sn_ov7648, + sn_ov7660 +}; + +static __u8 regsn20[] = { + 0x00, 0x2d, 0x46, 0x5a, 0x6c, 0x7c, 0x8b, 0x99, + 0xa6, 0xb2, 0xbf, 0xca, 0xd5, 0xe0, 0xeb, 0xf5, 0xff +}; +static __u8 regsn20_sn9c325[] = { + 0x0a, 0x3a, 0x56, 0x6c, 0x7e, 0x8d, 0x9a, 0xa4, + 0xaf, 0xbb, 0xc5, 0xcd, 0xd5, 0xde, 0xe8, 0xed, 0xf5 +}; + +static __u8 reg84[] = { + 0x14, 0x00, 0x27, 0x00, 0x07, 0x00, 0xe5, 0x0f, + 0xe4, 0x0f, 0x38, 0x00, 0x3e, 0x00, 0xc3, 0x0f, +/* 0x00, 0x00, 0x00, 0x00, 0x00 */ + 0xf7, 0x0f, 0x0a, 0x00, 0x00 +}; +static __u8 reg84_sn9c325[] = { + 0x14, 0x00, 0x27, 0x00, 0x07, 0x00, 0xe4, 0x0f, + 0xd3, 0x0f, 0x4b, 0x00, 0x48, 0x00, 0xc0, 0x0f, + 0xf8, 0x0f, 0x00, 0x00, 0x00 +}; + +static __u8 hv7131r_sensor_init[][8] = { + {0xC1, 0x11, 0x01, 0x08, 0x01, 0x00, 0x00, 0x10}, + {0xB1, 0x11, 0x34, 0x17, 0x7F, 0x00, 0x00, 0x10}, + {0xD1, 0x11, 0x40, 0xFF, 0x7F, 0x7F, 0x7F, 0x10}, + {0x91, 0x11, 0x44, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x11, 0x14, 0x01, 0xE2, 0x02, 0x82, 0x10}, + {0x91, 0x11, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10}, + + {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xC1, 0x11, 0x25, 0x00, 0x61, 0xA8, 0x00, 0x10}, + {0xA1, 0x11, 0x30, 0x22, 0x00, 0x00, 0x00, 0x10}, + {0xC1, 0x11, 0x31, 0x20, 0x2E, 0x20, 0x00, 0x10}, + {0xC1, 0x11, 0x25, 0x00, 0xC3, 0x50, 0x00, 0x10}, + {0xA1, 0x11, 0x30, 0x07, 0x00, 0x00, 0x00, 0x10}, /* gain14 */ + {0xC1, 0x11, 0x31, 0x10, 0x10, 0x10, 0x00, 0x10}, /* r g b 101a10 */ + + {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x11, 0x21, 0xD0, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x11, 0x23, 0x09, 0x00, 0x00, 0x00, 0x10}, + + {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x11, 0x21, 0xD0, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x11, 0x23, 0x10, 0x00, 0x00, 0x00, 0x10}, + {0, 0, 0, 0, 0, 0, 0, 0} +}; +static __u8 mi0360_sensor_init[][8] = { + {0xB1, 0x5D, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, + {0xB1, 0x5D, 0x0D, 0x00, 0x01, 0x00, 0x00, 0x10}, + {0xB1, 0x5D, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x01, 0x00, 0x08, 0x00, 0x16, 0x10}, + {0xD1, 0x5D, 0x03, 0x01, 0xE2, 0x02, 0x82, 0x10}, + {0xD1, 0x5D, 0x05, 0x00, 0x09, 0x00, 0x53, 0x10}, + {0xB1, 0x5D, 0x0D, 0x00, 0x02, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x14, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xB1, 0x5D, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x24, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x26, 0x00, 0x00, 0x00, 0x24, 0x10}, + {0xD1, 0x5D, 0x2F, 0xF7, 0xB0, 0x00, 0x04, 0x10}, + {0xD1, 0x5D, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x33, 0x00, 0x00, 0x01, 0x00, 0x10}, + {0xB1, 0x5D, 0x3D, 0x06, 0x8F, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x40, 0x01, 0xE0, 0x00, 0xD1, 0x10}, + {0xB1, 0x5D, 0x44, 0x00, 0x82, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x58, 0x00, 0x78, 0x00, 0x43, 0x10}, + {0xD1, 0x5D, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x5E, 0x00, 0x00, 0xA3, 0x1D, 0x10}, + {0xB1, 0x5D, 0x62, 0x04, 0x11, 0x00, 0x00, 0x10}, + + {0xB1, 0x5D, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10}, + {0xB1, 0x5D, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10}, + {0xB1, 0x5D, 0x09, 0x00, 0x64, 0x00, 0x00, 0x10}, + {0xD1, 0x5D, 0x2B, 0x00, 0xA0, 0x00, 0xB0, 0x10}, + {0xD1, 0x5D, 0x2D, 0x00, 0xA0, 0x00, 0xA0, 0x10}, + + {0xB1, 0x5D, 0x0A, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor clck ?2 */ + {0xB1, 0x5D, 0x06, 0x00, 0x30, 0x00, 0x00, 0x10}, + {0xB1, 0x5D, 0x05, 0x00, 0x0A, 0x00, 0x00, 0x10}, + {0xB1, 0x5D, 0x09, 0x02, 0x35, 0x00, 0x00, 0x10}, /* exposure 2 */ + + {0xD1, 0x5D, 0x2B, 0x00, 0xB9, 0x00, 0xE3, 0x10}, + {0xD1, 0x5D, 0x2D, 0x00, 0x5f, 0x00, 0xB9, 0x10}, /* 42 */ +/* {0xB1, 0x5D, 0x35, 0x00, 0x67, 0x00, 0x00, 0x10}, * gain orig */ +/* {0xB1, 0x5D, 0x35, 0x00, 0x20, 0x00, 0x00, 0x10}, * gain */ + {0xB1, 0x5D, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10}, /* update */ + {0xB1, 0x5D, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor on */ + {0, 0, 0, 0, 0, 0, 0, 0} +}; +static __u8 mo4000_sensor_init[][8] = { + {0xa1, 0x21, 0x01, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x04, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x05, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x05, 0x04, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x06, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x06, 0x81, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x11, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x11, 0x20, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x11, 0x30, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x0f, 0x20, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10}, + {0, 0, 0, 0, 0, 0, 0, 0} +}; +static __u8 ov7660_sensor_init[][8] = { + {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset SCCB */ + {0xa1, 0x21, 0x12, 0x05, 0x00, 0x00, 0x00, 0x10}, + /* Outformat ?? rawRGB */ + {0xa1, 0x21, 0x13, 0xb8, 0x00, 0x00, 0x00, 0x10}, /* init COM8 */ +/* {0xd1, 0x21, 0x00, 0x01, 0x74, 0x92, 0x00, 0x10}, + * GAIN BLUE RED VREF */ + {0xd1, 0x21, 0x00, 0x01, 0x74, 0x74, 0x00, 0x10}, + /* GAIN BLUE RED VREF */ + {0xd1, 0x21, 0x04, 0x00, 0x7d, 0x62, 0x00, 0x10}, + /* COM 1 BAVE GEAVE AECHH */ + {0xb1, 0x21, 0x08, 0x83, 0x01, 0x00, 0x00, 0x10}, /* RAVE COM2 */ + {0xd1, 0x21, 0x0c, 0x00, 0x08, 0x04, 0x4f, 0x10}, /* COM 3 4 5 6 */ +/* {0xd1, 0x21, 0x10, 0x7f, 0x40, 0x05, 0xf8, 0x10}, + * AECH CLKRC COM7 COM8 */ + {0xd1, 0x21, 0x10, 0x7f, 0x40, 0x05, 0xff, 0x10}, + /* AECH CLKRC COM7 COM8 */ + {0xc1, 0x21, 0x14, 0x2c, 0x00, 0x02, 0x00, 0x10}, /* COM9 COM10 */ + {0xd1, 0x21, 0x17, 0x10, 0x60, 0x02, 0x7b, 0x10}, + /* HSTART HSTOP VSTRT VSTOP */ + {0xa1, 0x21, 0x1b, 0x02, 0x00, 0x00, 0x00, 0x10}, /* PSHFT */ + {0xb1, 0x21, 0x1e, 0x01, 0x0e, 0x00, 0x00, 0x10}, /* MVFP LAEC */ + {0xd1, 0x21, 0x20, 0x07, 0x07, 0x07, 0x07, 0x10}, + /* BOS GBOS GROS ROS (BGGR offset) */ +/* {0xd1, 0x21, 0x24, 0x68, 0x58, 0xd4, 0x80, 0x10}, + * AEW AEB VPT BBIAS */ + {0xd1, 0x21, 0x24, 0x78, 0x68, 0xd4, 0x80, 0x10}, + /* AEW AEB VPT BBIAS */ + {0xd1, 0x21, 0x28, 0x80, 0x30, 0x00, 0x00, 0x10}, + /* GbBIAS RSVD EXHCH EXHCL */ + {0xd1, 0x21, 0x2c, 0x80, 0x00, 0x00, 0x62, 0x10}, + /* RBIAS ADVFL ASDVFH YAVE */ + {0xc1, 0x21, 0x30, 0x08, 0x30, 0xb4, 0x00, 0x10}, + /* HSYST HSYEN HREF */ + {0xd1, 0x21, 0x33, 0x00, 0x07, 0x84, 0x00, 0x10}, /* reserved */ + {0xd1, 0x21, 0x37, 0x0c, 0x02, 0x43, 0x00, 0x10}, + /* ADC ACOM OFON TSLB */ + {0xd1, 0x21, 0x3b, 0x02, 0x6c, 0x19, 0x0e, 0x10}, + /* COM11 COM12 COM13 COM14 */ + {0xd1, 0x21, 0x3f, 0x41, 0xc1, 0x22, 0x08, 0x10}, + /* EDGE COM15 COM16 COM17 */ + {0xd1, 0x21, 0x43, 0xf0, 0x10, 0x78, 0xa8, 0x10}, /* reserved */ + {0xd1, 0x21, 0x47, 0x60, 0x80, 0x00, 0x00, 0x10}, /* reserved */ + {0xd1, 0x21, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x10}, /* reserved */ + {0xd1, 0x21, 0x4f, 0x46, 0x36, 0x0f, 0x17, 0x10}, /* MTX 1 2 3 4 */ + {0xd1, 0x21, 0x53, 0x7f, 0x96, 0x40, 0x40, 0x10}, /* MTX 5 6 7 8 */ + {0xb1, 0x21, 0x57, 0x40, 0x0f, 0x00, 0x00, 0x10}, /* MTX9 MTXS */ + {0xd1, 0x21, 0x59, 0xba, 0x9a, 0x22, 0xb9, 0x10}, /* reserved */ + {0xd1, 0x21, 0x5d, 0x9b, 0x10, 0xf0, 0x05, 0x10}, /* reserved */ + {0xa1, 0x21, 0x61, 0x60, 0x00, 0x00, 0x00, 0x10}, /* reserved */ + {0xd1, 0x21, 0x62, 0x00, 0x00, 0x50, 0x30, 0x10}, + /* LCC1 LCC2 LCC3 LCC4 */ + {0xa1, 0x21, 0x66, 0x00, 0x00, 0x00, 0x00, 0x10}, /* LCC5 */ + {0xd1, 0x21, 0x67, 0x80, 0x7a, 0x90, 0x80, 0x10}, + {0xa1, 0x21, 0x6b, 0x0a, 0x00, 0x00, 0x00, 0x10}, + /* band gap reference [0..3] DBLV */ + {0xd1, 0x21, 0x6c, 0x30, 0x48, 0x80, 0x74, 0x10}, /* gamma curve */ + {0xd1, 0x21, 0x70, 0x64, 0x60, 0x5c, 0x58, 0x10}, /* gamma curve */ + {0xd1, 0x21, 0x74, 0x54, 0x4c, 0x40, 0x38, 0x10}, /* gamma curve */ + {0xd1, 0x21, 0x78, 0x34, 0x30, 0x2f, 0x2b, 0x10}, /* gamma curve */ + {0xd1, 0x21, 0x7c, 0x03, 0x07, 0x17, 0x34, 0x10}, /* gamma curve */ + {0xd1, 0x21, 0x80, 0x41, 0x4d, 0x58, 0x63, 0x10}, /* gamma curve */ + {0xd1, 0x21, 0x84, 0x6e, 0x77, 0x87, 0x95, 0x10}, /* gamma curve */ + {0xc1, 0x21, 0x88, 0xaf, 0xc7, 0xdf, 0x00, 0x10}, /* gamma curve */ + {0xc1, 0x21, 0x8b, 0x99, 0x99, 0xcf, 0x00, 0x10}, /* reserved */ + {0xb1, 0x21, 0x92, 0x00, 0x00, 0x00, 0x00, 0x10}, +/****** (some exchanges in the win trace) ******/ + {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10}, + /* bits[3..0]reserved */ + {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10}, + /* VREF vertical frame ctrl */ + {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10}, /* 0x20 */ + {0xa1, 0x21, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x10}, +/* {0xb1, 0x21, 0x01, 0x78, 0x78, 0x00, 0x00, 0x10}, */ +/****** (some exchanges in the win trace) ******/ + {0xa1, 0x21, 0x93, 0x00, 0x00, 0x00, 0x00, 0x10},/* dummy line hight */ + {0xa1, 0x21, 0x92, 0x25, 0x00, 0x00, 0x00, 0x10},/* dummy line low */ + {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}, +/* {0xa1, 0x21, 0x02, 0x90, 0x00, 0x00, 0x00, 0x10}, */ +/****** (some exchanges in the win trace) ******/ +/**********startsensor KO if changed !!****/ + {0xa1, 0x21, 0x93, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x92, 0xff, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x21, 0x2b, 0xc3, 0x00, 0x00, 0x00, 0x10}, +/* here may start the isoc exchanges */ + {0, 0, 0, 0, 0, 0, 0, 0} +}; +/* reg0x04 reg0x07 reg 0x10 */ +/* expo = (COM1 & 0x02) | (AECHH & 0x2f <<10) [ (AECh << 2) */ + +static __u8 ov7648_sensor_init[][8] = { + {0xC1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00}, + {0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00}, + {0xC1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00}, + {0xA1, 0x6E, 0x3F, 0x20, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x6E, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x6E, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x6E, 0x04, 0x02, 0xB1, 0x02, 0x39, 0x10}, + {0xD1, 0x6E, 0x08, 0x00, 0x01, 0x00, 0x00, 0x10}, + {0xD1, 0x6E, 0x0C, 0x02, 0x7F, 0x01, 0xE0, 0x10}, + {0xD1, 0x6E, 0x12, 0x03, 0x02, 0x00, 0x03, 0x10}, + {0xD1, 0x6E, 0x16, 0x85, 0x40, 0x4A, 0x40, 0x10}, + {0xC1, 0x6E, 0x1A, 0x00, 0x80, 0x00, 0x00, 0x10}, + {0xD1, 0x6E, 0x1D, 0x08, 0x03, 0x00, 0x00, 0x10}, + {0xD1, 0x6E, 0x23, 0x00, 0xB0, 0x00, 0x94, 0x10}, + {0xD1, 0x6E, 0x27, 0x58, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x6E, 0x2D, 0x14, 0x35, 0x61, 0x84, 0x10}, + {0xD1, 0x6E, 0x31, 0xA2, 0xBD, 0xD8, 0xFF, 0x10}, + {0xD1, 0x6E, 0x35, 0x06, 0x1E, 0x12, 0x02, 0x10}, + {0xD1, 0x6E, 0x39, 0xAA, 0x53, 0x37, 0xD5, 0x10}, + {0xA1, 0x6E, 0x3D, 0xF2, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x6E, 0x3E, 0x00, 0x00, 0x80, 0x03, 0x10}, + {0xD1, 0x6E, 0x42, 0x03, 0x00, 0x00, 0x00, 0x10}, + {0xC1, 0x6E, 0x46, 0x00, 0x80, 0x80, 0x00, 0x10}, + {0xD1, 0x6E, 0x4B, 0x02, 0xEF, 0x08, 0xCD, 0x10}, + {0xD1, 0x6E, 0x4F, 0x00, 0xD0, 0x00, 0xA0, 0x10}, + {0xD1, 0x6E, 0x53, 0x01, 0xAA, 0x01, 0x40, 0x10}, + {0xD1, 0x6E, 0x5A, 0x50, 0x04, 0x30, 0x03, 0x10}, + {0xA1, 0x6E, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x6E, 0x5F, 0x10, 0x40, 0xFF, 0x00, 0x10}, + /* {0xD1, 0x6E, 0x63, 0x40, 0x40, 0x00, 0x00, 0x10}, + {0xD1, 0x6E, 0x67, 0x00, 0x00, 0x00, 0x00, 0x10}, + * This is currently setting a + * blue tint, and some things more , i leave it here for future test if + * somene is having problems with color on this sensor + {0xD1, 0x6E, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xD1, 0x6E, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xC1, 0x6E, 0x73, 0x10, 0x80, 0xEB, 0x00, 0x10}, + {0xA1, 0x6E, 0x1E, 0x03, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x6E, 0x15, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xC1, 0x6E, 0x16, 0x40, 0x40, 0x40, 0x00, 0x10}, + {0xA1, 0x6E, 0x1D, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x6E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x6E, 0x07, 0xB5, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x6E, 0x18, 0x6B, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x6E, 0x1D, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x6E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xA1, 0x6E, 0x07, 0xB8, 0x00, 0x00, 0x00, 0x10}, */ + {0xC1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00}, + {0xA1, 0x6E, 0x06, 0x03, 0x00, 0x00, 0x00, 0x10}, /* Bright... */ + {0xA1, 0x6E, 0x07, 0x66, 0x00, 0x00, 0x00, 0x10}, /* B.. */ + {0xC1, 0x6E, 0x1A, 0x03, 0x65, 0x90, 0x00, 0x10}, /* Bright/Witen....*/ +/* {0xC1, 0x6E, 0x16, 0x45, 0x40, 0x60, 0x00, 0x10}, * Bright/Witene */ + {0, 0, 0, 0, 0, 0, 0, 0} +}; + +static __u8 qtable4[] = { + 0x06, 0x04, 0x04, 0x06, 0x04, 0x04, 0x06, 0x06, 0x06, 0x06, 0x08, 0x06, + 0x06, 0x08, 0x0A, 0x11, + 0x0A, 0x0A, 0x08, 0x08, 0x0A, 0x15, 0x0F, 0x0F, 0x0C, 0x11, 0x19, 0x15, + 0x19, 0x19, 0x17, 0x15, + 0x17, 0x17, 0x1B, 0x1D, 0x25, 0x21, 0x1B, 0x1D, 0x23, 0x1D, 0x17, 0x17, + 0x21, 0x2E, 0x21, 0x23, + 0x27, 0x29, 0x2C, 0x2C, 0x2C, 0x19, 0x1F, 0x30, 0x32, 0x2E, 0x29, 0x32, + 0x25, 0x29, 0x2C, 0x29, + 0x06, 0x08, 0x08, 0x0A, 0x08, 0x0A, 0x13, 0x0A, 0x0A, 0x13, 0x29, 0x1B, + 0x17, 0x1B, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, + 0x29, 0x29, 0x29, 0x29 +}; + +static void reg_r(struct usb_device *dev, + __u16 value, + __u8 *buffer, int len) +{ + usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + 0, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, 0, + buffer, len, + 500); +} + +static void reg_w(struct usb_device *dev, + __u16 value, + __u8 *buffer, + int len) +{ + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + value, 0, + buffer, len, + 500); +} + +/* write 2 bytes */ +static void i2c_w2(struct gspca_dev *gspca_dev, + __u8 *buffer) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + __u8 mode[8]; + + /* is i2c ready */ + mode[0] = sd->i2c_ctrl_reg | (2 << 4); + mode[1] = sd->i2c_base; + mode[2] = buffer[0]; + mode[3] = buffer[1]; + mode[4] = 0; + mode[5] = 0; + mode[6] = 0; + mode[7] = 0x10; + reg_w(dev, 0x08, mode, 8); +} + +/* write 8 bytes */ +static void i2c_w8(struct usb_device *dev, __u8 *buffer) +{ + reg_w(dev, 0x08, buffer, 8); + msleep(1); +} + +/* read 5 bytes */ +static void i2c_r5(struct gspca_dev *gspca_dev, __u8 reg, + __u8 *buffer) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + __u8 mode[8]; + + mode[0] = sd->i2c_ctrl_reg | 0x10; + mode[1] = sd->i2c_base; + mode[2] = reg; + mode[3] = 0; + mode[4] = 0; + mode[5] = 0; + mode[6] = 0; + mode[7] = 0x10; + i2c_w8(dev, mode); + mode[0] = sd->i2c_ctrl_reg | (5 << 4) | 0x02; + mode[2] = 0; + i2c_w8(dev, mode); + reg_r(dev, 0x0a, buffer, 5); +} + +static int probesensor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + __u8 reg02; + static __u8 datasend[] = { 2, 0 }; + /* reg val1 val2 val3 val4 */ + __u8 datarecd[6]; + + i2c_w2(gspca_dev, datasend); +/* should write 0xa1 0x11 0x02 0x00 0x00 0x00 0x00 the 0x10 is add by i2cw */ + msleep(10); + reg02 = 0x66; + reg_w(dev, 0x02, ®02, 1); /* Gpio on */ + msleep(10); + i2c_r5(gspca_dev, 0, datarecd); /* read sensor id */ + if (datarecd[0] == 0x02 + && datarecd[1] == 0x09 + && datarecd[2] == 0x01 + && datarecd[3] == 0x00 + && datarecd[4] == 0x00) { + PDEBUG(D_PROBE, "Find Sensor sn9c102P HV7131R"); + sd->sensor = SENSOR_HV7131R; + return SENSOR_HV7131R; + } + PDEBUG(D_PROBE, "Find Sensor %d %d %d", + datarecd[0], datarecd[1], datarecd[2]); + PDEBUG(D_PROBE, "Sensor sn9c102P Not found"); + return -ENODEV; +} + +static int configure_gpio(struct gspca_dev *gspca_dev, + __u8 *sn9c1xx) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + __u8 data; + __u8 regF1; + __u8 *reg9a; + static __u8 reg9a_def[] = + {0x08, 0x40, 0x20, 0x10, 0x00, 0x04}; + static __u8 reg9a_sn9c120[] = /* from win trace */ + {0x00, 0x40, 0x38, 0x30, 0x00, 0x20}; + static __u8 reg9a_sn9c325[] = + {0x0a, 0x40, 0x38, 0x30, 0x00, 0x20}; + + + regF1 = 0x00; + reg_w(dev, 0xf1, ®F1, 1); + + reg_w(dev, 0x01, &sn9c1xx[0], 1); /*fixme:jfm was [1] en v1*/ + + /* configure gpio */ + reg_w(dev, 0x01, &sn9c1xx[1], 2); + reg_w(dev, 0x08, &sn9c1xx[8], 2); + reg_w(dev, 0x17, &sn9c1xx[0x17], 3); + switch (sd->customid) { + case SN9C325: + reg9a = reg9a_sn9c325; + break; + case SN9C120: + reg9a = reg9a_sn9c120; + break; + default: + reg9a = reg9a_def; + break; + } + reg_w(dev, 0x9a, reg9a, 6); + + data = 0x60; /*fixme:jfm 60 00 00 (3) */ + reg_w(dev, 0xd4, &data, 1); + + reg_w(dev, 0x03, &sn9c1xx[3], 0x0f); + + switch (sd->customid) { + case SN9C120: /* from win trace */ + data = 0x61; + reg_w(dev, 0x01, &data, 1); + data = 0x20; + reg_w(dev, 0x17, &data, 1); + data = 0x60; + reg_w(dev, 0x01, &data, 1); + break; + case SN9C325: + data = 0x43; + reg_w(dev, 0x01, &data, 1); + data = 0xae; + reg_w(dev, 0x17, &data, 1); + data = 0x42; + reg_w(dev, 0x01, &data, 1); + break; + default: + data = 0x43; + reg_w(dev, 0x01, &data, 1); + data = 0x61; + reg_w(dev, 0x17, &data, 1); + data = 0x42; + reg_w(dev, 0x01, &data, 1); + } + + if (sd->sensor == SENSOR_HV7131R) { + if (probesensor(gspca_dev) < 0) + return -ENODEV; + } + return 0; +} + +static void hv7131R_InitSensor(struct gspca_dev *gspca_dev) +{ + int i = 0; + struct usb_device *dev = gspca_dev->dev; + static __u8 SetSensorClk[] = /* 0x08 Mclk */ + { 0xa1, 0x11, 0x01, 0x18, 0x00, 0x00, 0x00, 0x10 }; + + while (hv7131r_sensor_init[i][0]) { + i2c_w8(dev, hv7131r_sensor_init[i]); + i++; + } + i2c_w8(dev, SetSensorClk); +} + +static void mi0360_InitSensor(struct gspca_dev *gspca_dev) +{ + int i = 0; + struct usb_device *dev = gspca_dev->dev; + + while (mi0360_sensor_init[i][0]) { + i2c_w8(dev, mi0360_sensor_init[i]); + i++; + } +} + +static void mo4000_InitSensor(struct gspca_dev *gspca_dev) +{ + int i = 0; + struct usb_device *dev = gspca_dev->dev; + + while (mo4000_sensor_init[i][0]) { + i2c_w8(dev, mo4000_sensor_init[i]); + i++; + } +} + +static void ov7648_InitSensor(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + int i = 0; + + while (ov7648_sensor_init[i][0]) { + i2c_w8(dev, ov7648_sensor_init[i]); + i++; + } +} + +static void ov7660_InitSensor(struct gspca_dev *gspca_dev) +{ + int i = 0; + struct usb_device *dev = gspca_dev->dev; + + while (ov7660_sensor_init[i][0]) { + i2c_w8(dev, ov7660_sensor_init[i]); + i++; + } +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + __u16 vendor; + __u16 product; + + vendor = id->idVendor; + product = id->idProduct; + sd->sensor = -1; + switch (vendor) { + case 0x0458: /* Genius */ +/* switch (product) { + case 0x7025: */ + sd->customid = SN9C120; + sd->sensor = SENSOR_MI0360; + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x5d; +/* break; + } */ + break; + case 0x045e: +/* switch (product) { + case 0x00f5: + case 0x00f7: */ + sd->customid = SN9C105; + sd->sensor = SENSOR_OV7660; + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x21; +/* break; + } */ + break; + case 0x0471: /* Philips */ +/* switch (product) { + case 0x0327: + case 0x0328: + case 0x0330: */ + sd->customid = SN9C105; + sd->sensor = SENSOR_MI0360; + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x5d; +/* break; + } */ + break; + case 0x0c45: /* Sonix */ + switch (product) { + case 0x6040: + sd->customid = SN9C102P; + sd->sensor = SENSOR_MI0360; /* from BW600.inf */ +/* sd->sensor = SENSOR_HV7131R; * gspcav1 value */ + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x11; + break; +/* case 0x607a: * from BW600.inf + sd->customid = SN9C102P; + sd->sensor = SENSOR_OV7648; + sd->i2c_ctrl_reg = 0x??; + sd->i2c_base = 0x??; + break; */ + case 0x607c: + sd->customid = SN9C102P; + sd->sensor = SENSOR_HV7131R; + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x11; + break; +/* case 0x607e: * from BW600.inf + sd->customid = SN9C102P; + sd->sensor = SENSOR_OV7630; + sd->i2c_ctrl_reg = 0x??; + sd->i2c_base = 0x??; + break; */ + case 0x60c0: + sd->customid = SN9C105; + sd->sensor = SENSOR_MI0360; + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x5d; + break; +/* case 0x60c8: * from BW600.inf + sd->customid = SN9C105; + sd->sensor = SENSOR_OM6801; + sd->i2c_ctrl_reg = 0x??; + sd->i2c_base = 0x??; + break; */ +/* case 0x60cc: * from BW600.inf + sd->customid = SN9C105; + sd->sensor = SENSOR_HV7131GP; + sd->i2c_ctrl_reg = 0x??; + sd->i2c_base = 0x??; + break; */ + case 0x60ec: + sd->customid = SN9C105; + sd->sensor = SENSOR_MO4000; + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x21; + break; +/* case 0x60ef: * from BW600.inf + sd->customid = SN9C105; + sd->sensor = SENSOR_ICM105C; + sd->i2c_ctrl_reg = 0x??; + sd->i2c_base = 0x??; + break; */ +/* case 0x60fa: * from BW600.inf + sd->customid = SN9C105; + sd->sensor = SENSOR_OV7648; + sd->i2c_ctrl_reg = 0x??; + sd->i2c_base = 0x??; + break; */ + case 0x60fb: + sd->customid = SN9C105; + sd->sensor = SENSOR_OV7660; + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x21; + break; + case 0x60fc: + sd->customid = SN9C105; + sd->sensor = SENSOR_HV7131R; + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x11; + break; +/* case 0x60fe: * from BW600.inf + sd->customid = SN9C105; + sd->sensor = SENSOR_OV7630; + sd->i2c_ctrl_reg = 0x??; + sd->i2c_base = 0x??; + break; */ +/* case 0x6108: * from BW600.inf + sd->customid = SN9C120; + sd->sensor = SENSOR_OM6801; + sd->i2c_ctrl_reg = 0x??; + sd->i2c_base = 0x??; + break; */ +/* case 0x6122: * from BW600.inf + sd->customid = SN9C110; + sd->sensor = SENSOR_ICM105C; + sd->i2c_ctrl_reg = 0x??; + sd->i2c_base = 0x??; + break; */ + case 0x612a: +/* sd->customid = SN9C110; * in BW600.inf */ + sd->customid = SN9C325; + sd->sensor = SENSOR_OV7648; + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x21; + break; +/* case 0x6123: * from BW600.inf + sd->customid = SN9C110; + sd->sensor = SENSOR_SanyoCCD; + sd->i2c_ctrl_reg = 0x??; + sd->i2c_base = 0x??; + break; */ + case 0x612c: + sd->customid = SN9C110; + sd->sensor = SENSOR_MO4000; + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x21; + break; +/* case 0x612e: * from BW600.inf + sd->customid = SN9C110; + sd->sensor = SENSOR_OV7630; + sd->i2c_ctrl_reg = 0x??; + sd->i2c_base = 0x??; + break; */ +/* case 0x612f: * from BW600.inf + sd->customid = SN9C110; + sd->sensor = SENSOR_ICM105C; + sd->i2c_ctrl_reg = 0x??; + sd->i2c_base = 0x??; + break; */ + case 0x6130: + sd->customid = SN9C120; + sd->sensor = SENSOR_MI0360; + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x5d; + break; + case 0x6138: + sd->customid = SN9C120; + sd->sensor = SENSOR_MO4000; + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x21; + break; +/* case 0x613a: * from BW600.inf + sd->customid = SN9C120; + sd->sensor = SENSOR_OV7648; + sd->i2c_ctrl_reg = 0x??; + sd->i2c_base = 0x??; + break; */ + case 0x613b: + sd->customid = SN9C120; + sd->sensor = SENSOR_OV7660; + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x21; + break; + case 0x613c: + sd->customid = SN9C120; + sd->sensor = SENSOR_HV7131R; + sd->i2c_ctrl_reg = 0x81; + sd->i2c_base = 0x11; + break; +/* case 0x613e: * from BW600.inf + sd->customid = SN9C120; + sd->sensor = SENSOR_OV7630; + sd->i2c_ctrl_reg = 0x??; + sd->i2c_base = 0x??; + break; */ + } + break; + } + if (sd->sensor < 0) { + PDEBUG(D_ERR, "Invalid vendor/product %04x:%04x", + vendor, product); + return -EINVAL; + } + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + sd->qindex = 4; /* set the quantization table */ + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; + sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value; + sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value; + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; +/* __u8 *sn9c1xx; */ + __u8 regF1; + __u8 regGpio[] = { 0x29, 0x74 }; + + /* setup a selector by customid */ + regF1 = 0x01; + reg_w(dev, 0xf1, ®F1, 1); + reg_r(dev, 0x00, ®F1, 1); /* -> regF1 = 0x00 */ + reg_w(dev, 0xf1, ®F1, 1); + reg_r(dev, 0x00, ®F1, 1); + switch (sd->customid) { + case SN9C102P: + if (regF1 != 0x11) + return -ENODEV; + reg_w(dev, 0x02, ®Gpio[1], 1); + break; + case SN9C105: + if (regF1 != 0x11) + return -ENODEV; + reg_w(dev, 0x02, regGpio, 2); + break; + case SN9C110: + if (regF1 != 0x12) + return -ENODEV; + regGpio[1] = 0x62; + reg_w(dev, 0x02, ®Gpio[1], 1); + break; + case SN9C120: + if (regF1 != 0x12) + return -ENODEV; + regGpio[1] = 0x70; + reg_w(dev, 0x02, regGpio, 2); + break; + default: +/* case SN9C325: */ + if (regF1 != 0x12) + return -ENODEV; + regGpio[1] = 0x62; + reg_w(dev, 0x02, ®Gpio[1], 1); + break; + } + + regF1 = 0x01; + reg_w(dev, 0xf1, ®F1, 1); + + return 0; +} + +static unsigned int setexposure(struct gspca_dev *gspca_dev, + unsigned int expo) +{ + struct sd *sd = (struct sd *) gspca_dev; + static __u8 doit[] = /* update sensor */ + { 0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10 }; + static __u8 sensorgo[] = /* sensor on */ + { 0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10 }; + static __u8 gainMo[] = + { 0xa1, 0x21, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1d }; + + switch (sd->sensor) { + case SENSOR_HV7131R: { + __u8 Expodoit[] = + { 0xc1, 0x11, 0x25, 0x07, 0x27, 0xc0, 0x00, 0x16 }; + + Expodoit[3] = expo >> 16; + Expodoit[4] = expo >> 8; + Expodoit[5] = expo; + i2c_w8(gspca_dev->dev, Expodoit); + break; + } + case SENSOR_MI0360: { + __u8 expoMi[] = /* exposure 0x0635 -> 4 fp/s 0x10 */ + { 0xb1, 0x5d, 0x09, 0x06, 0x35, 0x00, 0x00, 0x16 }; + + if (expo > 0x0635) + expo = 0x0635; + else if (expo < 0x0001) + expo = 0x0001; + expoMi[3] = expo >> 8; + expoMi[4] = expo; + i2c_w8(gspca_dev->dev, expoMi); + i2c_w8(gspca_dev->dev, doit); + i2c_w8(gspca_dev->dev, sensorgo); + break; + } + case SENSOR_MO4000: { + __u8 expoMof[] = + { 0xa1, 0x21, 0x0f, 0x20, 0x00, 0x00, 0x00, 0x10 }; + __u8 expoMo10[] = + { 0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10 }; + + if (expo > 0x1fff) + expo = 0x1fff; + else if (expo < 0x0001) + expo = 0x0001; + expoMof[3] = (expo & 0x03fc) >> 2; + i2c_w8(gspca_dev->dev, expoMof); + expoMo10[3] = ((expo & 0x1c00) >> 10) + | ((expo & 0x0003) << 4); + i2c_w8(gspca_dev->dev, expoMo10); + i2c_w8(gspca_dev->dev, gainMo); + PDEBUG(D_CONF," set exposure %d", + ((expoMo10[3] & 0x07) << 10) + | (expoMof[3] << 2) + | ((expoMo10[3] & 0x30) >> 4)); + break; + } + } + return expo; +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + unsigned int expo; + __u8 k2; + + switch (sd->sensor) { + case SENSOR_HV7131R: + expo = sd->brightness << 4; + if (expo > 0x002dc6c0) + expo = 0x002dc6c0; + else if (expo < 0x02a0) + expo = 0x02a0; + sd->exposure = setexposure(gspca_dev, expo); + break; + case SENSOR_MI0360: + expo = sd->brightness >> 4; + sd->exposure = setexposure(gspca_dev, expo); + break; + case SENSOR_MO4000: + expo = sd->brightness >> 4; + sd->exposure = setexposure(gspca_dev, expo); + break; + case SENSOR_OV7660: + return; /*jfm??*/ + } + + k2 = sd->brightness >> 10; + reg_w(gspca_dev->dev, 0x96, &k2, 1); +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 k2; + __u8 contrast[] = { 0x00, 0x00, 0x28, 0x00, 0x07, 0x00 }; + + if (sd->sensor == SENSOR_OV7660) + return; /*jfm??*/ + k2 = sd->contrast; + contrast[2] = k2; + contrast[0] = (k2 + 1) >> 1; + contrast[4] = (k2 + 1) / 5; + reg_w(gspca_dev->dev, 0x84, contrast, 6); +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 data; + int colour; + + colour = sd->colors - 128; + if (colour > 0) + data = (colour + 32) & 0x7f; /* blue */ + else + data = (-colour + 32) & 0x7f; /* red */ + reg_w(gspca_dev->dev, 0x05, &data, 1); +} + +/* -- start the camera -- */ +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + int i; + __u8 data; + __u8 reg1; + __u8 reg17; + __u8 *sn9c1xx; + int mode; + static __u8 DC29[] = { 0x6a, 0x50, 0x00, 0x00, 0x50, 0x3c }; + static __u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f }; + static __u8 CA[] = { 0x28, 0xd8, 0x14, 0xec }; + static __u8 CA_sn9c120[] = { 0x14, 0xec, 0x0a, 0xf6 }; /* SN9C120 */ + static __u8 CE[] = { 0x32, 0xdd, 0x2d, 0xdd }; /* MI0360 */ + static __u8 CE_sn9c325[] = + { 0x32, 0xdd, 0x32, 0xdd }; /* OV7648 - SN9C325 */ + + sn9c1xx = sn_tb[(int) sd->sensor]; + configure_gpio(gspca_dev, sn9c1xx); + +/*fixme:jfm this sequence should appear at end of sd_start */ +/* with + data = 0x44; + reg_w(dev, 0x01, &data, 1); */ + reg_w(dev, 0x15, &sn9c1xx[0x15], 1); + reg_w(dev, 0x16, &sn9c1xx[0x16], 1); + reg_w(dev, 0x12, &sn9c1xx[0x12], 1); + reg_w(dev, 0x13, &sn9c1xx[0x13], 1); + reg_w(dev, 0x18, &sn9c1xx[0x18], 1); + reg_w(dev, 0xd2, &DC29[0], 1); + reg_w(dev, 0xd3, &DC29[1], 1); + reg_w(dev, 0xc6, &DC29[2], 1); + reg_w(dev, 0xc7, &DC29[3], 1); + reg_w(dev, 0xc8, &DC29[4], 1); + reg_w(dev, 0xc9, &DC29[5], 1); +/*fixme:jfm end of ending sequence */ + reg_w(dev, 0x18, &sn9c1xx[0x18], 1); + if (sd->customid == SN9C325) + data = 0xae; + else + data = 0x60; + reg_w(dev, 0x17, &data, 1); + reg_w(dev, 0x05, &sn9c1xx[5], 1); + reg_w(dev, 0x07, &sn9c1xx[7], 1); + reg_w(dev, 0x06, &sn9c1xx[6], 1); + reg_w(dev, 0x14, &sn9c1xx[0x14], 1); + if (sd->customid == SN9C325) { + reg_w(dev, 0x20, regsn20_sn9c325, 0x11); + for (i = 0; i < 8; i++) + reg_w(dev, 0x84, reg84_sn9c325, 0x15); + data = 0x0a; + reg_w(dev, 0x9a, &data, 1); + data = 0x60; + reg_w(dev, 0x99, &data, 1); + } else { + reg_w(dev, 0x20, regsn20, 0x11); + for (i = 0; i < 8; i++) + reg_w(dev, 0x84, reg84, 0x15); + data = 0x08; + reg_w(dev, 0x9a, &data, 1); + data = 0x59; + reg_w(dev, 0x99, &data, 1); + } + + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode; + reg1 = 0x02; + reg17 = 0x61; + switch (sd->sensor) { + case SENSOR_HV7131R: + hv7131R_InitSensor(gspca_dev); + if (mode) + reg1 = 0x46; /* 320 clk 48Mhz */ + else + reg1 = 0x06; /* 640 clk 24Mz */ + break; + case SENSOR_MI0360: + mi0360_InitSensor(gspca_dev); + if (mode) + reg1 = 0x46; /* 320 clk 48Mhz */ + else + reg1 = 0x06; /* 640 clk 24Mz */ + break; + case SENSOR_MO4000: + mo4000_InitSensor(gspca_dev); + if (mode) { +/* reg1 = 0x46; * 320 clk 48Mhz 60fp/s */ + reg1 = 0x06; /* clk 24Mz */ + } else { + reg17 = 0x22; /* 640 MCKSIZE */ + reg1 = 0x06; /* 640 clk 24Mz */ + } + break; + case SENSOR_OV7648: + reg17 = 0xa2; + reg1 = 0x44; + ov7648_InitSensor(gspca_dev); +/* if (mode) + ; * 320x2... + else + ; * 640x... */ + break; + default: +/* case SENSOR_OV7660: */ + ov7660_InitSensor(gspca_dev); + if (mode) { +/* reg17 = 0x21; * 320 */ +/* reg1 = 0x44; */ + reg1 = 0x46; + } else { + reg17 = 0xa2; /* 640 */ + reg1 = 0x40; + } + break; + } + reg_w(dev, 0xc0, C0, 6); + switch (sd->customid) { + case SN9C120: /*jfm ?? */ + reg_w(dev, 0xca, CA_sn9c120, 4); + break; + default: + reg_w(dev, 0xca, CA, 4); + break; + } + switch (sd->customid) { + case SN9C120: /*jfm ?? */ + case SN9C325: + reg_w(dev, 0xce, CE_sn9c325, 4); + break; + default: + reg_w(dev, 0xce, CE, 4); + /* ?? {0x1e, 0xdd, 0x2d, 0xe7} */ + break; + } + + /* here change size mode 0 -> VGA; 1 -> CIF */ + data = 0x40 | sn9c1xx[0x18] | (mode << 4); + reg_w(dev, 0x18, &data, 1); + + reg_w(dev, 0x100, qtable4, 0x40); + reg_w(dev, 0x140, qtable4 + 0x40, 0x40); + + data = sn9c1xx[0x18] | (mode << 4); + reg_w(dev, 0x18, &data, 1); + + reg_w(dev, 0x17, ®17, 1); + reg_w(dev, 0x01, ®1, 1); + setbrightness(gspca_dev); + setcontrast(gspca_dev); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + static __u8 stophv7131[] = + { 0xa1, 0x11, 0x02, 0x09, 0x00, 0x00, 0x00, 0x10 }; + static __u8 stopmi0360[] = + { 0xb1, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10 }; + __u8 regF1; + __u8 data; + __u8 *sn9c1xx; + + data = 0x0b; + switch (sd->sensor) { + case SENSOR_HV7131R: + i2c_w8(dev, stophv7131); + data = 0x2b; + break; + case SENSOR_MI0360: + i2c_w8(dev, stopmi0360); + data = 0x29; + break; + case SENSOR_MO4000: + break; + case SENSOR_OV7648: + data = 0x29; + break; + default: +/* case SENSOR_OV7660: */ + break; + } + sn9c1xx = sn_tb[(int) sd->sensor]; + reg_w(dev, 0x01, &sn9c1xx[1], 1); + reg_w(dev, 0x17, &sn9c1xx[0x17], 1); + reg_w(dev, 0x01, &sn9c1xx[1], 1); + reg_w(dev, 0x01, &data, 1); + regF1 = 0x01; + reg_w(dev, 0xf1, ®F1, 1); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void setautogain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + /* Thanks S., without your advice, autobright should not work :) */ + int delta; + int expotimes = 0; + __u8 luma_mean = 130; + __u8 luma_delta = 20; + + delta = sd->avg_lum; + if (delta < luma_mean - luma_delta || + delta > luma_mean + luma_delta) { + switch (sd->sensor) { + case SENSOR_HV7131R: + expotimes = sd->exposure >> 8; + expotimes += (luma_mean - delta) >> 4; + if (expotimes < 0) + expotimes = 0; + sd->exposure = setexposure(gspca_dev, + (unsigned int) (expotimes << 8)); + break; + case SENSOR_MO4000: + case SENSOR_MI0360: + expotimes = sd->exposure; + expotimes += (luma_mean - delta) >> 6; + if (expotimes < 0) + expotimes = 0; + sd->exposure = setexposure(gspca_dev, + (unsigned int) expotimes); + setcolors(gspca_dev); + break; + } + } +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + unsigned char *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + int sof, avg_lum; + + sof = len - 64; + if (sof >= 0 && data[sof] == 0xff && data[sof + 1] == 0xd9) { + + /* end of frame */ + gspca_frame_add(gspca_dev, LAST_PACKET, + frame, data, sof + 2); + if (sd->ag_cnt < 0) + return; + if (--sd->ag_cnt >= 0) + return; + sd->ag_cnt = AG_CNT_START; +/* w1 w2 w3 */ +/* w4 w5 w6 */ +/* w7 w8 */ +/* w4 */ + avg_lum = ((data[sof + 29] << 8) | data[sof + 30]) >> 6; +/* w6 */ + avg_lum += ((data[sof + 33] << 8) | data[sof + 34]) >> 6; +/* w2 */ + avg_lum += ((data[sof + 25] << 8) | data[sof + 26]) >> 6; +/* w8 */ + avg_lum += ((data[sof + 37] << 8) | data[sof + 38]) >> 6; +/* w5 */ + avg_lum += ((data[sof + 31] << 8) | data[sof + 32]) >> 4; + avg_lum >>= 4; + sd->avg_lum = avg_lum; + PDEBUG(D_PACK, "mean lum %d", avg_lum); + setautogain(gspca_dev); + return; + } + if (gspca_dev->last_packet_type == LAST_PACKET) { + + /* put the JPEG 422 header */ + jpeg_put_header(gspca_dev, frame, sd->qindex, 0x21); + } + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); +} + +static unsigned int getexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 hexpo, mexpo, lexpo; + __u8 expo[6]; + + switch (sd->sensor) { + case SENSOR_HV7131R: + /* read sensor exposure */ + i2c_r5(gspca_dev, 0x25, expo); + return (expo[0] << 16) | (expo[1] << 8) | expo[2]; + case SENSOR_MI0360: + /* read sensor exposure */ + i2c_r5(gspca_dev, 0x09, expo); + return (expo[0] << 8) | expo[1]; + case SENSOR_MO4000: + i2c_r5(gspca_dev, 0x0e, expo); + hexpo = 0; /* expo[1] & 0x07; */ + mexpo = 0x40; /* expo[2] &0xff; */ + lexpo = (expo[1] & 0x30) >> 4; + PDEBUG(D_CONF, "exposure %d", + (hexpo << 10) | (mexpo << 2) | lexpo); + return (hexpo << 10) | (mexpo << 2) | lexpo; + default: +/* case SENSOR_OV7660: */ + /* read sensor exposure */ + i2c_r5(gspca_dev, 0x04, expo); + hexpo = expo[3] & 0x2f; + lexpo = expo[0] & 0x02; + i2c_r5(gspca_dev, 0x08, expo); + mexpo = expo[2]; + return (hexpo << 10) | (mexpo << 2) | lexpo; + } +} + +static void getbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* hardcoded registers seem not readable */ + switch (sd->sensor) { + case SENSOR_HV7131R: +/* sd->brightness = 0x7fff; */ + sd->brightness = getexposure(gspca_dev) >> 4; + break; + case SENSOR_MI0360: + sd->brightness = getexposure(gspca_dev) << 4; + break; + case SENSOR_MO4000: +/* sd->brightness = 0x1fff; */ + sd->brightness = getexposure(gspca_dev) << 4; + break; + } +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->colors; + return 0; +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + if (val) + sd->ag_cnt = AG_CNT_START; + else + sd->ag_cnt = -1; + return 0; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x0458, 0x7025), DVNM("Genius Eye 311Q")}, + {USB_DEVICE(0x045e, 0x00f5), DVNM("MicroSoft VX3000")}, + {USB_DEVICE(0x045e, 0x00f7), DVNM("MicroSoft VX1000")}, + {USB_DEVICE(0x0471, 0x0327), DVNM("Philips SPC 600 NC")}, + {USB_DEVICE(0x0471, 0x0328), DVNM("Philips SPC 700 NC")}, + {USB_DEVICE(0x0471, 0x0330), DVNM("Philips SPC 710NC")}, + {USB_DEVICE(0x0c45, 0x6040), DVNM("Speed NVC 350K")}, + {USB_DEVICE(0x0c45, 0x607c), DVNM("Sonix sn9c102p Hv7131R")}, + {USB_DEVICE(0x0c45, 0x60c0), DVNM("Sangha Sn535")}, + {USB_DEVICE(0x0c45, 0x60ec), DVNM("SN9C105+MO4000")}, + {USB_DEVICE(0x0c45, 0x60fb), DVNM("Surfer NoName")}, + {USB_DEVICE(0x0c45, 0x60fc), DVNM("LG-LIC300")}, + {USB_DEVICE(0x0c45, 0x612a), DVNM("Avant Camera")}, + {USB_DEVICE(0x0c45, 0x612c), DVNM("Typhoon Rasy Cam 1.3MPix")}, + {USB_DEVICE(0x0c45, 0x6130), DVNM("Sonix Pccam")}, + {USB_DEVICE(0x0c45, 0x6138), DVNM("Sn9c120 Mo4000")}, + {USB_DEVICE(0x0c45, 0x613b), DVNM("Surfer SN-206")}, + {USB_DEVICE(0x0c45, 0x613c), DVNM("Sonix Pccam168")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + info("v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + info("deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c new file mode 100644 index 000000000000..c0dd969a3106 --- /dev/null +++ b/drivers/media/video/gspca/spca500.c @@ -0,0 +1,1195 @@ +/* + * SPCA500 chip based cameras initialization data + * + * V4L2 by Jean-Francois Moine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define MODULE_NAME "spca500" + +#include "gspca.h" +#include "jpeg.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/SPCA500 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + unsigned char packet[ISO_MAX_SIZE + 128]; + /* !! no more than 128 ff in an ISO packet */ + + unsigned char brightness; + unsigned char contrast; + unsigned char colors; + + char qindex; + char subtype; +#define AgfaCl20 0 +#define AiptekPocketDV 1 +#define BenqDC1016 2 +#define CreativePCCam300 3 +#define DLinkDSC350 4 +#define Gsmartmini 5 +#define IntelPocketPCCamera 6 +#define KodakEZ200 7 +#define LogitechClickSmart310 8 +#define LogitechClickSmart510 9 +#define LogitechTraveler 10 +#define MustekGsmart300 11 +#define Optimedia 12 +#define PalmPixDC85 13 +#define ToptroIndus 14 +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x7f, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define SD_COLOR 2 + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Color", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, +}; + +static struct cam_mode vga_mode[] = { + {V4L2_PIX_FMT_JPEG, 320, 240, 1}, + {V4L2_PIX_FMT_JPEG, 640, 480, 0}, +}; + +static struct cam_mode sif_mode[] = { + {V4L2_PIX_FMT_JPEG, 176, 144, 1}, + {V4L2_PIX_FMT_JPEG, 352, 288, 0}, +}; + +/* Frame packet header offsets for the spca500 */ +#define SPCA500_OFFSET_PADDINGLB 2 +#define SPCA500_OFFSET_PADDINGHB 3 +#define SPCA500_OFFSET_MODE 4 +#define SPCA500_OFFSET_IMGWIDTH 5 +#define SPCA500_OFFSET_IMGHEIGHT 6 +#define SPCA500_OFFSET_IMGMODE 7 +#define SPCA500_OFFSET_QTBLINDEX 8 +#define SPCA500_OFFSET_FRAMSEQ 9 +#define SPCA500_OFFSET_CDSPINFO 10 +#define SPCA500_OFFSET_GPIO 11 +#define SPCA500_OFFSET_AUGPIO 12 +#define SPCA500_OFFSET_DATA 16 + + +static __u16 spca500_visual_defaults[][3] = { + {0x00, 0x0003, 0x816b}, /* SSI not active sync with vsync, + * hue (H byte) = 0, + * saturation/hue enable, + * brightness/contrast enable. + */ + {0x00, 0x0000, 0x8167}, /* brightness = 0 */ + {0x00, 0x0020, 0x8168}, /* contrast = 0 */ + {0x00, 0x0003, 0x816b}, /* SSI not active sync with vsync, + * hue (H byte) = 0, saturation/hue enable, + * brightness/contrast enable. + * was 0x0003, now 0x0000. + */ + {0x00, 0x0000, 0x816a}, /* hue (L byte) = 0 */ + {0x00, 0x0020, 0x8169}, /* saturation = 0x20 */ + {0x00, 0x0050, 0x8157}, /* edge gain high threshold */ + {0x00, 0x0030, 0x8158}, /* edge gain low threshold */ + {0x00, 0x0028, 0x8159}, /* edge bandwidth high threshold */ + {0x00, 0x000a, 0x815a}, /* edge bandwidth low threshold */ + {0x00, 0x0001, 0x8202}, /* clock rate compensation = 1/25 sec/frame */ + {0x0c, 0x0004, 0x0000}, + /* set interface */ + + {0, 0, 0} +}; +static __u16 Clicksmart510_defaults[][3] = { + {0x00, 0x00, 0x8211}, + {0x00, 0x01, 0x82c0}, + {0x00, 0x10, 0x82cb}, + {0x00, 0x0f, 0x800d}, + {0x00, 0x82, 0x8225}, + {0x00, 0x21, 0x8228}, + {0x00, 0x00, 0x8203}, + {0x00, 0x00, 0x8204}, + {0x00, 0x08, 0x8205}, + {0x00, 0xf8, 0x8206}, + {0x00, 0x28, 0x8207}, + {0x00, 0xa0, 0x8208}, + {0x00, 0x08, 0x824a}, + {0x00, 0x08, 0x8214}, + {0x00, 0x80, 0x82c1}, + {0x00, 0x00, 0x82c2}, + {0x00, 0x00, 0x82ca}, + {0x00, 0x80, 0x82c1}, + {0x00, 0x04, 0x82c2}, + {0x00, 0x00, 0x82ca}, + {0x00, 0xfc, 0x8100}, + {0x00, 0xfc, 0x8105}, + {0x00, 0x30, 0x8101}, + {0x00, 0x00, 0x8102}, + {0x00, 0x00, 0x8103}, + {0x00, 0x66, 0x8107}, + {0x00, 0x00, 0x816b}, + {0x00, 0x00, 0x8155}, + {0x00, 0x01, 0x8156}, + {0x00, 0x60, 0x8157}, + {0x00, 0x40, 0x8158}, + {0x00, 0x0a, 0x8159}, + {0x00, 0x06, 0x815a}, + {0x00, 0x00, 0x813f}, + {0x00, 0x00, 0x8200}, + {0x00, 0x19, 0x8201}, + {0x00, 0x00, 0x82c1}, + {0x00, 0xa0, 0x82c2}, + {0x00, 0x00, 0x82ca}, + {0x00, 0x00, 0x8117}, + {0x00, 0x00, 0x8118}, + {0x00, 0x65, 0x8119}, + {0x00, 0x00, 0x811a}, + {0x00, 0x00, 0x811b}, + {0x00, 0x55, 0x811c}, + {0x00, 0x65, 0x811d}, + {0x00, 0x55, 0x811e}, + {0x00, 0x16, 0x811f}, + {0x00, 0x19, 0x8120}, + {0x00, 0x80, 0x8103}, + {0x00, 0x83, 0x816b}, + {0x00, 0x25, 0x8168}, + {0x00, 0x01, 0x820f}, + {0x00, 0xff, 0x8115}, + {0x00, 0x48, 0x8116}, + {0x00, 0x50, 0x8151}, + {0x00, 0x40, 0x8152}, + {0x00, 0x78, 0x8153}, + {0x00, 0x40, 0x8154}, + {0x00, 0x00, 0x8167}, + {0x00, 0x20, 0x8168}, + {0x00, 0x00, 0x816a}, + {0x00, 0x03, 0x816b}, + {0x00, 0x20, 0x8169}, + {0x00, 0x60, 0x8157}, + {0x00, 0x00, 0x8190}, + {0x00, 0x00, 0x81a1}, + {0x00, 0x00, 0x81b2}, + {0x00, 0x27, 0x8191}, + {0x00, 0x27, 0x81a2}, + {0x00, 0x27, 0x81b3}, + {0x00, 0x4b, 0x8192}, + {0x00, 0x4b, 0x81a3}, + {0x00, 0x4b, 0x81b4}, + {0x00, 0x66, 0x8193}, + {0x00, 0x66, 0x81a4}, + {0x00, 0x66, 0x81b5}, + {0x00, 0x79, 0x8194}, + {0x00, 0x79, 0x81a5}, + {0x00, 0x79, 0x81b6}, + {0x00, 0x8a, 0x8195}, + {0x00, 0x8a, 0x81a6}, + {0x00, 0x8a, 0x81b7}, + {0x00, 0x9b, 0x8196}, + {0x00, 0x9b, 0x81a7}, + {0x00, 0x9b, 0x81b8}, + {0x00, 0xa6, 0x8197}, + {0x00, 0xa6, 0x81a8}, + {0x00, 0xa6, 0x81b9}, + {0x00, 0xb2, 0x8198}, + {0x00, 0xb2, 0x81a9}, + {0x00, 0xb2, 0x81ba}, + {0x00, 0xbe, 0x8199}, + {0x00, 0xbe, 0x81aa}, + {0x00, 0xbe, 0x81bb}, + {0x00, 0xc8, 0x819a}, + {0x00, 0xc8, 0x81ab}, + {0x00, 0xc8, 0x81bc}, + {0x00, 0xd2, 0x819b}, + {0x00, 0xd2, 0x81ac}, + {0x00, 0xd2, 0x81bd}, + {0x00, 0xdb, 0x819c}, + {0x00, 0xdb, 0x81ad}, + {0x00, 0xdb, 0x81be}, + {0x00, 0xe4, 0x819d}, + {0x00, 0xe4, 0x81ae}, + {0x00, 0xe4, 0x81bf}, + {0x00, 0xed, 0x819e}, + {0x00, 0xed, 0x81af}, + {0x00, 0xed, 0x81c0}, + {0x00, 0xf7, 0x819f}, + {0x00, 0xf7, 0x81b0}, + {0x00, 0xf7, 0x81c1}, + {0x00, 0xff, 0x81a0}, + {0x00, 0xff, 0x81b1}, + {0x00, 0xff, 0x81c2}, + {0x00, 0x03, 0x8156}, + {0x00, 0x00, 0x8211}, + {0x00, 0x20, 0x8168}, + {0x00, 0x01, 0x8202}, + {0x00, 0x30, 0x8101}, + {0x00, 0x00, 0x8111}, + {0x00, 0x00, 0x8112}, + {0x00, 0x00, 0x8113}, + {0x00, 0x00, 0x8114}, + {} +}; + +static unsigned char qtable_creative_pccam[2][64] = { + { /* Q-table Y-components */ + 0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12, + 0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11, + 0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11, + 0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13, + 0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17, + 0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c, + 0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e, + 0x16, 0x1c, 0x1d, 0x1d, 0x22, 0x1e, 0x1f, 0x1e}, + { /* Q-table C-components */ + 0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, + 0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e} +}; + +static unsigned char qtable_kodak_ez200[2][64] = { + { /* Q-table Y-components */ + 0x02, 0x01, 0x01, 0x02, 0x02, 0x04, 0x05, 0x06, + 0x01, 0x01, 0x01, 0x02, 0x03, 0x06, 0x06, 0x06, + 0x01, 0x01, 0x02, 0x02, 0x04, 0x06, 0x07, 0x06, + 0x01, 0x02, 0x02, 0x03, 0x05, 0x09, 0x08, 0x06, + 0x02, 0x02, 0x04, 0x06, 0x07, 0x0b, 0x0a, 0x08, + 0x02, 0x04, 0x06, 0x06, 0x08, 0x0a, 0x0b, 0x09, + 0x05, 0x06, 0x08, 0x09, 0x0a, 0x0c, 0x0c, 0x0a, + 0x07, 0x09, 0x0a, 0x0a, 0x0b, 0x0a, 0x0a, 0x0a}, + { /* Q-table C-components */ + 0x02, 0x02, 0x02, 0x05, 0x0a, 0x0a, 0x0a, 0x0a, + 0x02, 0x02, 0x03, 0x07, 0x0a, 0x0a, 0x0a, 0x0a, + 0x02, 0x03, 0x06, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x05, 0x07, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a} +}; + +static unsigned char qtable_pocketdv[2][64] = { + { /* Q-table Y-components start registers 0x8800 */ + 0x06, 0x04, 0x04, 0x06, 0x0a, 0x10, 0x14, 0x18, + 0x05, 0x05, 0x06, 0x08, 0x0a, 0x17, 0x18, 0x16, + 0x06, 0x05, 0x06, 0x0a, 0x10, 0x17, 0x1c, 0x16, + 0x06, 0x07, 0x09, 0x0c, 0x14, 0x23, 0x20, 0x19, + 0x07, 0x09, 0x0f, 0x16, 0x1b, 0x2c, 0x29, 0x1f, + 0x0a, 0x0e, 0x16, 0x1a, 0x20, 0x2a, 0x2d, 0x25, + 0x14, 0x1a, 0x1f, 0x23, 0x29, 0x30, 0x30, 0x28, + 0x1d, 0x25, 0x26, 0x27, 0x2d, 0x28, 0x29, 0x28, + }, + { /* Q-table C-components start registers 0x8840 */ + 0x07, 0x07, 0x0a, 0x13, 0x28, 0x28, 0x28, 0x28, + 0x07, 0x08, 0x0a, 0x1a, 0x28, 0x28, 0x28, 0x28, + 0x0a, 0x0a, 0x16, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x13, 0x1a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28} +}; + +static void spca5xxRegRead(struct usb_device *dev, + __u16 index, + __u8 *buffer, __u16 length) +{ + usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + 0, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, buffer, length, 500); +} + +static int reg_write(struct usb_device *dev, + __u16 req, __u16 index, __u16 value) +{ + int ret; + + ret = usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + req, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + PDEBUG(D_USBO, "reg write: [0x%02x] = 0x%02x, 0x%x", + index, value, ret); + if (ret < 0) + PDEBUG(D_ERR, "reg write: error %d", ret); + return ret; +} + +/* returns: negative is error, pos or zero is data */ +static int reg_read(struct usb_device *dev, + __u16 req, /* bRequest */ + __u16 index, /* wIndex */ + __u16 length) /* wLength (1 or 2 only) */ +{ + int ret; + __u8 buf[2]; + + buf[1] = 0; + ret = usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, + buf, length, + 500); /* timeout */ + if (ret < 0) { + PDEBUG(D_ERR, "reg_read err %d", ret); + return -1; + } + return (buf[1] << 8) + buf[0]; +} + +/* + * Simple function to wait for a given 8-bit value to be returned from + * a reg_read call. + * Returns: negative is error or timeout, zero is success. + */ +static int reg_readwait(struct usb_device *dev, + __u16 reg, __u16 index, __u16 value) +{ + int ret, cnt = 20; + + while (--cnt > 0) { + ret = reg_read(dev, reg, index, 1); + if (ret == value) + return 0; + msleep(50); + } + return -EIO; +} + +static int write_vector(struct gspca_dev *gspca_dev, + __u16 data[][3]) +{ + struct usb_device *dev = gspca_dev->dev; + int ret, i = 0; + + while (data[i][0] != 0 || data[i][1] != 0 || data[i][2] != 0) { + ret = reg_write(dev, data[i][0], data[i][2], data[i][1]); + if (ret < 0) + return ret; + i++; + } + return 0; +} + +static int spca50x_setup_qtable(struct gspca_dev *gspca_dev, + unsigned int request, + unsigned int ybase, + unsigned int cbase, + unsigned char qtable[2][64]) +{ + struct usb_device *dev = gspca_dev->dev; + int i, err; + + /* loop over y components */ + for (i = 0; i < 64; i++) { + err = reg_write(dev, request, ybase + i, qtable[0][i]); + if (err < 0) + return err; + } + + /* loop over c components */ + for (i = 0; i < 64; i++) { + err = reg_write(dev, request, cbase + i, qtable[1][i]); + if (err < 0) + return err; + } + return 0; +} + +static void spca500_ping310(struct gspca_dev *gspca_dev) +{ + __u8 Data[2]; + + spca5xxRegRead(gspca_dev->dev, 0x0d04, Data, 2); + PDEBUG(D_PACK, "ClickSmart310 ping 0x0d04 0x%02x 0x%02x", + Data[0], Data[1]); +} + +static void spca500_clksmart310_init(struct gspca_dev *gspca_dev) +{ + __u8 Data[2]; + + spca5xxRegRead(gspca_dev->dev, 0x0d05, Data, 2); + PDEBUG(D_PACK, "ClickSmart310 init 0x0d05 0x%02x 0x%02x", Data[0], + Data[1]); + reg_write(gspca_dev->dev, 0x00, 0x8167, 0x5a); + spca500_ping310(gspca_dev); + + reg_write(gspca_dev->dev, 0x00, 0x8168, 0x22); + reg_write(gspca_dev->dev, 0x00, 0x816a, 0xc0); + reg_write(gspca_dev->dev, 0x00, 0x816b, 0x0b); + reg_write(gspca_dev->dev, 0x00, 0x8169, 0x25); + reg_write(gspca_dev->dev, 0x00, 0x8157, 0x5b); + reg_write(gspca_dev->dev, 0x00, 0x8158, 0x5b); + reg_write(gspca_dev->dev, 0x00, 0x813f, 0x03); + reg_write(gspca_dev->dev, 0x00, 0x8151, 0x4a); + reg_write(gspca_dev->dev, 0x00, 0x8153, 0x78); + reg_write(gspca_dev->dev, 0x00, 0x0d01, 0x04); + /* 00 for adjust shutter */ + reg_write(gspca_dev->dev, 0x00, 0x0d02, 0x01); + reg_write(gspca_dev->dev, 0x00, 0x8169, 0x25); + reg_write(gspca_dev->dev, 0x00, 0x0d01, 0x02); +} + +static void spca500_setmode(struct gspca_dev *gspca_dev, + __u8 xmult, __u8 ymult) +{ + int mode; + + /* set x multiplier */ + reg_write(gspca_dev->dev, 0, 0x8001, xmult); + + /* set y multiplier */ + reg_write(gspca_dev->dev, 0, 0x8002, ymult); + + /* use compressed mode, VGA, with mode specific subsample */ + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode; + reg_write(gspca_dev->dev, 0, 0x8003, mode << 4); +} + +static int spca500_full_reset(struct gspca_dev *gspca_dev) +{ + int err; + + /* send the reset command */ + err = reg_write(gspca_dev->dev, 0xe0, 0x0001, 0x0000); + if (err < 0) + return err; + + /* wait for the reset to complete */ + err = reg_readwait(gspca_dev->dev, 0x06, 0x0000, 0x0000); + if (err < 0) + return err; + err = reg_write(gspca_dev->dev, 0xe0, 0x0000, 0x0000); + if (err < 0) + return err; + err = reg_readwait(gspca_dev->dev, 0x06, 0, 0); + if (err < 0) { + PDEBUG(D_ERR, "reg_readwait() failed"); + return err; + } + /* all ok */ + return 0; +} + +/* Synchro the Bridge with sensor */ +/* Maybe that will work on all spca500 chip */ +/* because i only own a clicksmart310 try for that chip */ +/* using spca50x_set_packet_size() cause an Ooops here */ +/* usb_set_interface from kernel 2.6.x clear all the urb stuff */ +/* up-port the same feature as in 2.4.x kernel */ +static int spca500_synch310(struct gspca_dev *gspca_dev) +{ + __u8 Data; + + if (usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0) < 0) { + PDEBUG(D_ERR, "Set packet size: set interface error"); + goto error; + } + spca500_ping310(gspca_dev); + + spca5xxRegRead(gspca_dev->dev, 0x0d00, &Data, 1); + + /* need alt setting here */ + PDEBUG(D_PACK, "ClickSmart310 sync alt: %d", gspca_dev->alt); + + /* Windoze use pipe with altsetting 6 why 7 here */ + if (usb_set_interface(gspca_dev->dev, + gspca_dev->iface, + gspca_dev->alt) < 0) { + PDEBUG(D_ERR, "Set packet size: set interface error"); + goto error; + } + return 0; +error: + return -EBUSY; +} + +static void spca500_reinit(struct gspca_dev *gspca_dev) +{ + int err; + __u8 Data; + + /* some unknow command from Aiptek pocket dv and family300 */ + + reg_write(gspca_dev->dev, 0x00, 0x0d01, 0x01); + reg_write(gspca_dev->dev, 0x00, 0x0d03, 0x00); + reg_write(gspca_dev->dev, 0x00, 0x0d02, 0x01); + + /* enable drop packet */ + reg_write(gspca_dev->dev, 0x00, 0x850a, 0x0001); + + err = spca50x_setup_qtable(gspca_dev, 0x00, 0x8800, 0x8840, + qtable_pocketdv); + if (err < 0) + PDEBUG(D_ERR|D_STREAM, "spca50x_setup_qtable failed on init"); + + /* set qtable index */ + reg_write(gspca_dev->dev, 0x00, 0x8880, 2); + /* family cam Quicksmart stuff */ + reg_write(gspca_dev->dev, 0x00, 0x800a, 0x00); + /* Set agc transfer: synced inbetween frames */ + reg_write(gspca_dev->dev, 0x00, 0x820f, 0x01); + /* Init SDRAM - needed for SDRAM access */ + reg_write(gspca_dev->dev, 0x00, 0x870a, 0x04); + /*Start init sequence or stream */ + + reg_write(gspca_dev->dev, 0, 0x8003, 0x00); + /* switch to video camera mode */ + reg_write(gspca_dev->dev, 0x00, 0x8000, 0x0004); + msleep(2000); + if (reg_readwait(gspca_dev->dev, 0, 0x8000, 0x44) != 0) + spca5xxRegRead(gspca_dev->dev, 0x816b, &Data, 1); + reg_write(gspca_dev->dev, 0x00, 0x816b, Data); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + __u16 vendor; + __u16 product; + + vendor = id->idVendor; + product = id->idProduct; + switch (vendor) { + case 0x040a: /* Kodak cameras */ +/* switch (product) { */ +/* case 0x0300: */ + sd->subtype = KodakEZ200; +/* break; */ +/* } */ + break; + case 0x041e: /* Creative cameras */ +/* switch (product) { */ +/* case 0x400a: */ + sd->subtype = CreativePCCam300; +/* break; */ +/* } */ + break; + case 0x046d: /* Logitech Labtec */ + switch (product) { + case 0x0890: + sd->subtype = LogitechTraveler; + break; + case 0x0900: + sd->subtype = LogitechClickSmart310; + break; + case 0x0901: + sd->subtype = LogitechClickSmart510; + break; + } + break; + case 0x04a5: /* Benq */ +/* switch (product) { */ +/* case 0x300c: */ + sd->subtype = BenqDC1016; +/* break; */ +/* } */ + break; + case 0x04fc: /* SunPlus */ +/* switch (product) { */ +/* case 0x7333: */ + sd->subtype = PalmPixDC85; +/* break; */ +/* } */ + break; + case 0x055f: /* Mustek cameras */ + switch (product) { + case 0xc200: + sd->subtype = MustekGsmart300; + break; + case 0xc220: + sd->subtype = Gsmartmini; + break; + } + break; + case 0x06bd: /* Agfa Cl20 */ +/* switch (product) { */ +/* case 0x0404: */ + sd->subtype = AgfaCl20; +/* break; */ +/* } */ + break; + case 0x06be: /* Optimedia */ +/* switch (product) { */ +/* case 0x0800: */ + sd->subtype = Optimedia; +/* break; */ +/* } */ + break; + case 0x084d: /* D-Link / Minton */ +/* switch (product) { */ +/* case 0x0003: * DSC-350 / S-Cam F5 */ + sd->subtype = DLinkDSC350; +/* break; */ +/* } */ + break; + case 0x08ca: /* Aiptek */ +/* switch (product) { */ +/* case 0x0103: */ + sd->subtype = AiptekPocketDV; +/* break; */ +/* } */ + break; + case 0x2899: /* ToptroIndustrial */ +/* switch (product) { */ +/* case 0x012c: */ + sd->subtype = ToptroIndus; +/* break; */ +/* } */ + break; + case 0x8086: /* Intel */ +/* switch (product) { */ +/* case 0x0630: * Pocket PC Camera */ + sd->subtype = IntelPocketPCCamera; +/* break; */ +/* } */ + break; + } + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + if (sd->subtype != LogitechClickSmart310) { + cam->cam_mode = vga_mode; + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + } else { + cam->cam_mode = sif_mode; + cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; + } + sd->qindex = 5; + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; + sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value; + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* initialisation of spca500 based cameras is deferred */ + PDEBUG(D_STREAM, "SPCA500 init"); + if (sd->subtype == LogitechClickSmart310) + spca500_clksmart310_init(gspca_dev); +/* else + spca500_initialise(gspca_dev); */ + PDEBUG(D_STREAM, "SPCA500 init done"); + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int err; + __u8 Data; + __u8 xmult, ymult; + + if (sd->subtype == LogitechClickSmart310) { + xmult = 0x16; + ymult = 0x12; + } else { + xmult = 0x28; + ymult = 0x1e; + } + + /* is there a sensor here ? */ + spca5xxRegRead(gspca_dev->dev, 0x8a04, &Data, 1); + PDEBUG(D_STREAM, "Spca500 Sensor Address 0x%02X", Data); + PDEBUG(D_STREAM, "Spca500 curr_mode: %d Xmult: 0x%02X, Ymult: 0x%02X", + gspca_dev->curr_mode, xmult, ymult); + + /* setup qtable */ + switch (sd->subtype) { + case LogitechClickSmart310: + spca500_setmode(gspca_dev, xmult, ymult); + + /* enable drop packet */ + reg_write(gspca_dev->dev, 0x00, 0x850a, 0x0001); + reg_write(gspca_dev->dev, 0x00, 0x8880, 3); + err = spca50x_setup_qtable(gspca_dev, + 0x00, 0x8800, 0x8840, + qtable_creative_pccam); + if (err < 0) + PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + /* Init SDRAM - needed for SDRAM access */ + reg_write(gspca_dev->dev, 0x00, 0x870a, 0x04); + + /* switch to video camera mode */ + reg_write(gspca_dev->dev, 0x00, 0x8000, 0x0004); + msleep(500); + if (reg_readwait(gspca_dev->dev, 0, 0x8000, 0x44) != 0) + PDEBUG(D_ERR, "reg_readwait() failed"); + + spca5xxRegRead(gspca_dev->dev, 0x816b, &Data, 1); + reg_write(gspca_dev->dev, 0x00, 0x816b, Data); + + spca500_synch310(gspca_dev); + + write_vector(gspca_dev, spca500_visual_defaults); + spca500_setmode(gspca_dev, xmult, ymult); + /* enable drop packet */ + reg_write(gspca_dev->dev, 0x00, 0x850a, 0x0001); + PDEBUG(D_ERR, "failed to enable drop packet"); + reg_write(gspca_dev->dev, 0x00, 0x8880, 3); + err = spca50x_setup_qtable(gspca_dev, + 0x00, 0x8800, 0x8840, + qtable_creative_pccam); + if (err < 0) + PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + + /* Init SDRAM - needed for SDRAM access */ + reg_write(gspca_dev->dev, 0x00, 0x870a, 0x04); + + /* switch to video camera mode */ + reg_write(gspca_dev->dev, 0x00, 0x8000, 0x0004); + + if (reg_readwait(gspca_dev->dev, 0, 0x8000, 0x44) != 0) + PDEBUG(D_ERR, "reg_readwait() failed"); + + spca5xxRegRead(gspca_dev->dev, 0x816b, &Data, 1); + reg_write(gspca_dev->dev, 0x00, 0x816b, Data); + break; + case CreativePCCam300: /* Creative PC-CAM 300 640x480 CCD */ + case IntelPocketPCCamera: /* FIXME: Temporary fix for + * Intel Pocket PC Camera + * - NWG (Sat 29th March 2003) */ + + /* do a full reset */ + if ((err = spca500_full_reset(gspca_dev)) < 0) + PDEBUG(D_ERR, "spca500_full_reset failed"); + + /* enable drop packet */ + err = reg_write(gspca_dev->dev, 0x00, 0x850a, 0x0001); + if (err < 0) + PDEBUG(D_ERR, "failed to enable drop packet"); + reg_write(gspca_dev->dev, 0x00, 0x8880, 3); + err = spca50x_setup_qtable(gspca_dev, + 0x00, 0x8800, 0x8840, + qtable_creative_pccam); + if (err < 0) + PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + + spca500_setmode(gspca_dev, xmult, ymult); + reg_write(gspca_dev->dev, 0x20, 0x0001, 0x0004); + + /* switch to video camera mode */ + reg_write(gspca_dev->dev, 0x00, 0x8000, 0x0004); + + if (reg_readwait(gspca_dev->dev, 0, 0x8000, 0x44) != 0) + PDEBUG(D_ERR, "reg_readwait() failed"); + + spca5xxRegRead(gspca_dev->dev, 0x816b, &Data, 1); + reg_write(gspca_dev->dev, 0x00, 0x816b, Data); + + /* write_vector(gspca_dev, spca500_visual_defaults); */ + break; + case KodakEZ200: /* Kodak EZ200 */ + + /* do a full reset */ + err = spca500_full_reset(gspca_dev); + if (err < 0) + PDEBUG(D_ERR, "spca500_full_reset failed"); + /* enable drop packet */ + reg_write(gspca_dev->dev, 0x00, 0x850a, 0x0001); + reg_write(gspca_dev->dev, 0x00, 0x8880, 0); + err = spca50x_setup_qtable(gspca_dev, + 0x00, 0x8800, 0x8840, + qtable_kodak_ez200); + if (err < 0) + PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + spca500_setmode(gspca_dev, xmult, ymult); + + reg_write(gspca_dev->dev, 0x20, 0x0001, 0x0004); + + /* switch to video camera mode */ + reg_write(gspca_dev->dev, 0x00, 0x8000, 0x0004); + + if (reg_readwait(gspca_dev->dev, 0, 0x8000, 0x44) != 0) + PDEBUG(D_ERR, "reg_readwait() failed"); + + spca5xxRegRead(gspca_dev->dev, 0x816b, &Data, 1); + reg_write(gspca_dev->dev, 0x00, 0x816b, Data); + + /* write_vector(gspca_dev, spca500_visual_defaults); */ + break; + + case BenqDC1016: + case DLinkDSC350: /* FamilyCam 300 */ + case AiptekPocketDV: /* Aiptek PocketDV */ + case Gsmartmini: /*Mustek Gsmart Mini */ + case MustekGsmart300: /* Mustek Gsmart 300 */ + case PalmPixDC85: + case Optimedia: + case ToptroIndus: + case AgfaCl20: + spca500_reinit(gspca_dev); + reg_write(gspca_dev->dev, 0x00, 0x0d01, 0x01); + /* enable drop packet */ + reg_write(gspca_dev->dev, 0x00, 0x850a, 0x0001); + + err = spca50x_setup_qtable(gspca_dev, + 0x00, 0x8800, 0x8840, qtable_pocketdv); + if (err < 0) + PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + reg_write(gspca_dev->dev, 0x00, 0x8880, 2); + + /* familycam Quicksmart pocketDV stuff */ + reg_write(gspca_dev->dev, 0x00, 0x800a, 0x00); + /* Set agc transfer: synced inbetween frames */ + reg_write(gspca_dev->dev, 0x00, 0x820f, 0x01); + /* Init SDRAM - needed for SDRAM access */ + reg_write(gspca_dev->dev, 0x00, 0x870a, 0x04); + + spca500_setmode(gspca_dev,xmult,ymult); + /* switch to video camera mode */ + reg_write(gspca_dev->dev, 0x00, 0x8000, 0x0004); + + reg_readwait(gspca_dev->dev, 0, 0x8000, 0x44); + + spca5xxRegRead(gspca_dev->dev, 0x816b, &Data, 1); + reg_write(gspca_dev->dev, 0x00, 0x816b, Data); + break; + case LogitechTraveler: + case LogitechClickSmart510: + reg_write(gspca_dev->dev, 0x02, 0x00, 0x00); + /* enable drop packet */ + reg_write(gspca_dev->dev, 0x00, 0x850a, 0x0001); + + err = spca50x_setup_qtable(gspca_dev, + 0x00, 0x8800, + 0x8840, qtable_creative_pccam); + if (err < 0) + PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + reg_write(gspca_dev->dev, 0x00, 0x8880, 3); + reg_write(gspca_dev->dev, 0x00, 0x800a, 0x00); + /* Init SDRAM - needed for SDRAM access */ + reg_write(gspca_dev->dev, 0x00, 0x870a, 0x04); + + spca500_setmode(gspca_dev, xmult, ymult); + + /* switch to video camera mode */ + reg_write(gspca_dev->dev, 0x00, 0x8000, 0x0004); + reg_readwait(gspca_dev->dev, 0, 0x8000, 0x44); + + spca5xxRegRead(gspca_dev->dev, 0x816b, &Data, 1); + reg_write(gspca_dev->dev, 0x00, 0x816b, Data); + write_vector(gspca_dev, Clicksmart510_defaults); + break; + } +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + __u8 data = 0; + + reg_write(gspca_dev->dev, 0, 0x8003, 0x00); + + /* switch to video camera mode */ + reg_write(gspca_dev->dev, 0x00, 0x8000, 0x0004); + spca5xxRegRead(gspca_dev->dev, 0x8000, &data, 1); + PDEBUG(D_STREAM, "stop SPCA500 done reg8000: 0x%2x", data); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + unsigned char *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + unsigned char *s, *d; + static unsigned char ffd9[] = {0xff, 0xd9}; + +/* frames are jpeg 4.1.1 without 0xff escape */ + if (data[0] == 0xff) { + if (data[1] != 0x01) { /* drop packet */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + ffd9, 2); + + /* put the JPEG header in the new frame */ + jpeg_put_header(gspca_dev, frame, + ((struct sd *) gspca_dev)->qindex, + 0x22); + + data += SPCA500_OFFSET_DATA; + len -= SPCA500_OFFSET_DATA; + } else { + data += 1; + len -= 1; + } + + /* add 0x00 after 0xff */ + for (i = len; --i >= 0; ) + if (data[i] == 0xff) + break; + if (i < 0) { /* no 0xff */ + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + return; + } + s = data; + d = sd->packet; + for (i = 0; i < len; i++) { + *d++ = *s++; + if (s[-1] == 0xff) + *d++ = 0x00; + } + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + sd->packet, d - sd->packet); +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_write(gspca_dev->dev, 0x00, 0x8167, + (__u8) (sd->brightness - 128)); +} + +static void getbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = reg_read(gspca_dev->dev, 0x00, 0x8167, 1) + 128; +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_write(gspca_dev->dev, 0x00, 0x8168, sd->contrast >> 2); +} + +static void getcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = reg_read(gspca_dev->dev, 0x0, 0x8168, 1) << 2; +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_write(gspca_dev->dev, 0x00, 0x8169, sd->colors >> 2); +} + +static void getcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = reg_read(gspca_dev->dev, 0x0, 0x8169, 1) << 2; +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcontrast(gspca_dev); + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcolors(gspca_dev); + *val = sd->colors; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = sizeof sd_ctrls / sizeof sd_ctrls[0], + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x040a, 0x0300), DVNM("Kodak EZ200")}, + {USB_DEVICE(0x041e, 0x400a), DVNM("Creative PC-CAM 300")}, + {USB_DEVICE(0x046d, 0x0890), DVNM("Logitech QuickCam traveler")}, + {USB_DEVICE(0x046d, 0x0900), DVNM("Logitech Inc. ClickSmart 310")}, + {USB_DEVICE(0x046d, 0x0901), DVNM("Logitech Inc. ClickSmart 510")}, + {USB_DEVICE(0x04a5, 0x300c), DVNM("Benq DC1016")}, + {USB_DEVICE(0x04fc, 0x7333), DVNM("PalmPixDC85")}, + {USB_DEVICE(0x055f, 0xc200), DVNM("Mustek Gsmart 300")}, + {USB_DEVICE(0x055f, 0xc220), DVNM("Gsmart Mini")}, + {USB_DEVICE(0x06bd, 0x0404), DVNM("Agfa CL20")}, + {USB_DEVICE(0x06be, 0x0800), DVNM("Optimedia")}, + {USB_DEVICE(0x084d, 0x0003), DVNM("D-Link DSC-350")}, + {USB_DEVICE(0x08ca, 0x0103), DVNM("Aiptek PocketDV")}, + {USB_DEVICE(0x2899, 0x012c), DVNM("Toptro Industrial")}, + {USB_DEVICE(0x8086, 0x0630), DVNM("Intel Pocket PC Camera")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/video/gspca/spca501.c new file mode 100644 index 000000000000..c6468cf3506a --- /dev/null +++ b/drivers/media/video/gspca/spca501.c @@ -0,0 +1,2219 @@ +/* + * SPCA501 chip based cameras initialization data + * + * V4L2 by Jean-Francois Moine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define MODULE_NAME "spca501" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/SPCA501 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + unsigned short contrast; + __u8 brightness; + __u8 colors; + + char subtype; +#define Arowana300KCMOSCamera 0 +#define IntelCreateAndShare 1 +#define KodakDVC325 2 +#define MystFromOriUnknownCamera 3 +#define SmileIntlCamera 4 +#define ThreeComHomeConnectLite 5 +#define ViewQuestM318B 6 +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define MY_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 63, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define MY_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 0xffff, + .step = 1, + .default_value = 0xaa00, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define MY_COLOR 2 + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Color", + .minimum = 0, + .maximum = 63, + .step = 1, + .default_value = 31, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, +}; + +static struct cam_mode vga_mode[] = { + {V4L2_PIX_FMT_SPCA501, 160, 120, 2}, + {V4L2_PIX_FMT_SPCA501, 320, 240, 1}, + {V4L2_PIX_FMT_SPCA501, 640, 480, 0}, +}; + +#define SPCA50X_REG_USB 0x2 /* spca505 501 */ +/* + * Data to initialize a SPCA501. From a capture file provided by Bill Roehl + * With SPCA501 chip description + */ +#define CCDSP_SET /* set CCDSP parameters */ +#define TG_SET /* set time generator set */ +#undef DSPWIN_SET /* set DSP windows parameters */ +#undef ALTER_GAMA /* Set alternate set to YUV transform coeffs. */ +#define SPCA501_SNAPBIT 0x80 +#define SPCA501_SNAPCTRL 0x10 +/* Frame packet header offsets for the spca501 */ +#define SPCA501_OFFSET_GPIO 1 +#define SPCA501_OFFSET_TYPE 2 +#define SPCA501_OFFSET_TURN3A 3 +#define SPCA501_OFFSET_FRAMSEQ 4 +#define SPCA501_OFFSET_COMPRESS 5 +#define SPCA501_OFFSET_QUANT 6 +#define SPCA501_OFFSET_QUANT2 7 +#define SPCA501_OFFSET_DATA 8 + +#define SPCA501_PROP_COMP_ENABLE(d) ((d) & 1) +#define SPCA501_PROP_SNAP(d) ((d) & 0x40) +#define SPCA501_PROP_SNAP_CTRL(d) ((d) & 0x10) +#define SPCA501_PROP_COMP_THRESH(d) (((d) & 0x0e) >> 1) +#define SPCA501_PROP_COMP_QUANT(d) (((d) & 0x70) >> 4) + +/* SPCA501 CCDSP control */ +#define SPCA501_REG_CCDSP 0x01 +/* SPCA501 control/status registers */ +#define SPCA501_REG_CTLRL 0x02 + +/* registers for color correction and YUV transformation */ +#define SPCA501_A11 0x08 +#define SPCA501_A12 0x09 +#define SPCA501_A13 0x0A +#define SPCA501_A21 0x0B +#define SPCA501_A22 0x0C +#define SPCA501_A23 0x0D +#define SPCA501_A31 0x0E +#define SPCA501_A32 0x0F +#define SPCA501_A33 0x10 + +/* Data for video camera initialization before capturing */ +static __u16 spca501_open_data[][3] = { + /* bmRequest,value,index */ + + {0x2, 0x50, 0x00}, /* C/S enable soft reset */ + {0x2, 0x40, 0x00}, /* C/S disable soft reset */ + {0x2, 0x02, 0x05}, /* C/S general purpose I/O data */ + {0x2, 0x03, 0x05}, /* C/S general purpose I/O data */ + +#ifdef CCDSP_SET + {0x1, 0x38, 0x01}, /* CCDSP options */ + {0x1, 0x05, 0x02}, /* CCDSP Optical black level for user settings */ + {0x1, 0xC0, 0x03}, /* CCDSP Optical black settings */ + + {0x1, 0x67, 0x07}, + {0x1, 0x63, 0x3f}, /* CCDSP CCD gamma enable */ + {0x1, 0x03, 0x56}, /* Add gamma correction */ + + {0x1, 0xFF, 0x15}, /* CCDSP High luminance for white balance */ + {0x1, 0x01, 0x16}, /* CCDSP Low luminance for white balance */ + +/* Color correction and RGB-to-YUV transformation coefficients changing */ +#ifdef ALTER_GAMA + {0x0, 0x00, 0x08}, /* A11 */ + {0x0, 0x00, 0x09}, /* A12 */ + {0x0, 0x90, 0x0A}, /* A13 */ + {0x0, 0x12, 0x0B}, /* A21 */ + {0x0, 0x00, 0x0C}, /* A22 */ + {0x0, 0x00, 0x0D}, /* A23 */ + {0x0, 0x00, 0x0E}, /* A31 */ + {0x0, 0x02, 0x0F}, /* A32 */ + {0x0, 0x00, 0x10}, /* A33 */ +#else + {0x1, 0x2a, 0x08}, /* A11 0x31 */ + {0x1, 0xf8, 0x09}, /* A12 f8 */ + {0x1, 0xf8, 0x0A}, /* A13 f8 */ + {0x1, 0xf8, 0x0B}, /* A21 f8 */ + {0x1, 0x14, 0x0C}, /* A22 0x14 */ + {0x1, 0xf8, 0x0D}, /* A23 f8 */ + {0x1, 0xf8, 0x0E}, /* A31 f8 */ + {0x1, 0xf8, 0x0F}, /* A32 f8 */ + {0x1, 0x20, 0x10}, /* A33 0x20 */ +#endif + {0x1, 0x00, 0x11}, /* R offset */ + {0x1, 0x00, 0x12}, /* G offset */ + {0x1, 0x00, 0x13}, /* B offset */ + {0x1, 0x00, 0x14}, /* GB offset */ + +#endif + +#ifdef TG_SET + /* Time generator manipulations */ + {0x0, 0xfc, 0x0}, /* Set up high bits of shutter speed */ + {0x0, 0x01, 0x1}, /* Set up low bits of shutter speed */ + + {0x0, 0xe4, 0x04}, /* DCLK*2 clock phase adjustment */ + {0x0, 0x08, 0x05}, /* ADCK phase adjustment, inv. ext. VB */ + {0x0, 0x03, 0x06}, /* FR phase adjustment */ + {0x0, 0x01, 0x07}, /* FCDS phase adjustment */ + {0x0, 0x39, 0x08}, /* FS phase adjustment */ + {0x0, 0x88, 0x0a}, /* FH1 phase and delay adjustment */ + {0x0, 0x03, 0x0f}, /* pixel identification */ + {0x0, 0x00, 0x11}, /* clock source selection (default) */ + + /*VERY strange manipulations with + * select DMCLP or OBPX to be ADCLP output (0x0C) + * OPB always toggle or not (0x0D) but they allow + * us to set up brightness + */ + {0x0, 0x01, 0x0c}, + {0x0, 0xe0, 0x0d}, + /* Done */ +#endif + +#ifdef DSPWIN_SET + {0x1, 0xa0, 0x01}, /* Setting image processing parameters */ + {0x1, 0x1c, 0x17}, /* Changing Windows positions X1 */ + {0x1, 0xe2, 0x19}, /* X2 */ + {0x1, 0x1c, 0x1b}, /* X3 */ + {0x1, 0xe2, 0x1d}, /* X4 */ + {0x1, 0x5f, 0x1f}, /* X5 */ + {0x1, 0x32, 0x20}, /* Y5 */ + {0x1, 0x01, 0x10}, /* Changing A33 */ +#endif + + {0x2, 0x204a, 0x07},/* Setting video compression & resolution 160x120 */ + {0x2, 0x94, 0x06}, /* Setting video no compression */ + {} +}; + +/* + The SPCAxxx docs from Sunplus document these values + in tables, one table per register number. In the data + below, dmRequest is the register number, index is the Addr, + and value is a combination of Bit values. + Bit Value (hex) + 0 01 + 1 02 + 2 04 + 3 08 + 4 10 + 5 20 + 6 40 + 7 80 + */ + +/* Data for chip initialization (set default values) */ +static __u16 spca501_init_data[][3] = { + /* Set all the values to powerup defaults */ + /* bmRequest,value,index */ + {0x0, 0xAA, 0x00}, + {0x0, 0x02, 0x01}, + {0x0, 0x01, 0x02}, + {0x0, 0x02, 0x03}, + {0x0, 0xCE, 0x04}, + {0x0, 0x00, 0x05}, + {0x0, 0x00, 0x06}, + {0x0, 0x00, 0x07}, + {0x0, 0x00, 0x08}, + {0x0, 0x00, 0x09}, + {0x0, 0x90, 0x0A}, + {0x0, 0x12, 0x0B}, + {0x0, 0x00, 0x0C}, + {0x0, 0x00, 0x0D}, + {0x0, 0x00, 0x0E}, + {0x0, 0x02, 0x0F}, + {0x0, 0x00, 0x10}, + {0x0, 0x00, 0x11}, + {0x0, 0x00, 0x12}, + {0x0, 0x00, 0x13}, + {0x0, 0x00, 0x14}, + {0x0, 0x00, 0x15}, + {0x0, 0x00, 0x16}, + {0x0, 0x00, 0x17}, + {0x0, 0x00, 0x18}, + {0x0, 0x00, 0x19}, + {0x0, 0x00, 0x1A}, + {0x0, 0x00, 0x1B}, + {0x0, 0x00, 0x1C}, + {0x0, 0x00, 0x1D}, + {0x0, 0x00, 0x1E}, + {0x0, 0x00, 0x1F}, + {0x0, 0x00, 0x20}, + {0x0, 0x00, 0x21}, + {0x0, 0x00, 0x22}, + {0x0, 0x00, 0x23}, + {0x0, 0x00, 0x24}, + {0x0, 0x00, 0x25}, + {0x0, 0x00, 0x26}, + {0x0, 0x00, 0x27}, + {0x0, 0x00, 0x28}, + {0x0, 0x00, 0x29}, + {0x0, 0x00, 0x2A}, + {0x0, 0x00, 0x2B}, + {0x0, 0x00, 0x2C}, + {0x0, 0x00, 0x2D}, + {0x0, 0x00, 0x2E}, + {0x0, 0x00, 0x2F}, + {0x0, 0x00, 0x30}, + {0x0, 0x00, 0x31}, + {0x0, 0x00, 0x32}, + {0x0, 0x00, 0x33}, + {0x0, 0x00, 0x34}, + {0x0, 0x00, 0x35}, + {0x0, 0x00, 0x36}, + {0x0, 0x00, 0x37}, + {0x0, 0x00, 0x38}, + {0x0, 0x00, 0x39}, + {0x0, 0x00, 0x3A}, + {0x0, 0x00, 0x3B}, + {0x0, 0x00, 0x3C}, + {0x0, 0x00, 0x3D}, + {0x0, 0x00, 0x3E}, + {0x0, 0x00, 0x3F}, + {0x0, 0x00, 0x40}, + {0x0, 0x00, 0x41}, + {0x0, 0x00, 0x42}, + {0x0, 0x00, 0x43}, + {0x0, 0x00, 0x44}, + {0x0, 0x00, 0x45}, + {0x0, 0x00, 0x46}, + {0x0, 0x00, 0x47}, + {0x0, 0x00, 0x48}, + {0x0, 0x00, 0x49}, + {0x0, 0x00, 0x4A}, + {0x0, 0x00, 0x4B}, + {0x0, 0x00, 0x4C}, + {0x0, 0x00, 0x4D}, + {0x0, 0x00, 0x4E}, + {0x0, 0x00, 0x4F}, + {0x0, 0x00, 0x50}, + {0x0, 0x00, 0x51}, + {0x0, 0x00, 0x52}, + {0x0, 0x00, 0x53}, + {0x0, 0x00, 0x54}, + {0x0, 0x00, 0x55}, + {0x0, 0x00, 0x56}, + {0x0, 0x00, 0x57}, + {0x0, 0x00, 0x58}, + {0x0, 0x00, 0x59}, + {0x0, 0x00, 0x5A}, + {0x0, 0x00, 0x5B}, + {0x0, 0x00, 0x5C}, + {0x0, 0x00, 0x5D}, + {0x0, 0x00, 0x5E}, + {0x0, 0x00, 0x5F}, + {0x0, 0x00, 0x60}, + {0x0, 0x00, 0x61}, + {0x0, 0x00, 0x62}, + {0x0, 0x00, 0x63}, + {0x0, 0x00, 0x64}, + {0x0, 0x00, 0x65}, + {0x0, 0x00, 0x66}, + {0x0, 0x00, 0x67}, + {0x0, 0x00, 0x68}, + {0x0, 0x00, 0x69}, + {0x0, 0x00, 0x6A}, + {0x0, 0x00, 0x6B}, + {0x0, 0x00, 0x6C}, + {0x0, 0x00, 0x6D}, + {0x0, 0x00, 0x6E}, + {0x0, 0x00, 0x6F}, + {0x0, 0x00, 0x70}, + {0x0, 0x00, 0x71}, + {0x0, 0x00, 0x72}, + {0x0, 0x00, 0x73}, + {0x0, 0x00, 0x74}, + {0x0, 0x00, 0x75}, + {0x0, 0x00, 0x76}, + {0x0, 0x00, 0x77}, + {0x0, 0x00, 0x78}, + {0x0, 0x00, 0x79}, + {0x0, 0x00, 0x7A}, + {0x0, 0x00, 0x7B}, + {0x0, 0x00, 0x7C}, + {0x0, 0x00, 0x7D}, + {0x0, 0x00, 0x7E}, + {0x0, 0x00, 0x7F}, + {0x0, 0x00, 0x80}, + {0x0, 0x00, 0x81}, + {0x0, 0x00, 0x82}, + {0x0, 0x00, 0x83}, + {0x0, 0x00, 0x84}, + {0x0, 0x00, 0x85}, + {0x0, 0x00, 0x86}, + {0x0, 0x00, 0x87}, + {0x0, 0x00, 0x88}, + {0x0, 0x00, 0x89}, + {0x0, 0x00, 0x8A}, + {0x0, 0x00, 0x8B}, + {0x0, 0x00, 0x8C}, + {0x0, 0x00, 0x8D}, + {0x0, 0x00, 0x8E}, + {0x0, 0x00, 0x8F}, + {0x0, 0x00, 0x90}, + {0x0, 0x00, 0x91}, + {0x0, 0x00, 0x92}, + {0x0, 0x00, 0x93}, + {0x0, 0x00, 0x94}, + {0x0, 0x00, 0x95}, + {0x0, 0x00, 0x96}, + {0x0, 0x00, 0x97}, + {0x0, 0x00, 0x98}, + {0x0, 0x00, 0x99}, + {0x0, 0x00, 0x9A}, + {0x0, 0x00, 0x9B}, + {0x0, 0x00, 0x9C}, + {0x0, 0x00, 0x9D}, + {0x0, 0x00, 0x9E}, + {0x0, 0x00, 0x9F}, + {0x0, 0x00, 0xA0}, + {0x0, 0x00, 0xA1}, + {0x0, 0x00, 0xA2}, + {0x0, 0x00, 0xA3}, + {0x0, 0x00, 0xA4}, + {0x0, 0x00, 0xA5}, + {0x0, 0x00, 0xA6}, + {0x0, 0x00, 0xA7}, + {0x0, 0x00, 0xA8}, + {0x0, 0x00, 0xA9}, + {0x0, 0x00, 0xAA}, + {0x0, 0x00, 0xAB}, + {0x0, 0x00, 0xAC}, + {0x0, 0x00, 0xAD}, + {0x0, 0x00, 0xAE}, + {0x0, 0x00, 0xAF}, + {0x0, 0x00, 0xB0}, + {0x0, 0x00, 0xB1}, + {0x0, 0x00, 0xB2}, + {0x0, 0x00, 0xB3}, + {0x0, 0x00, 0xB4}, + {0x0, 0x00, 0xB5}, + {0x0, 0x00, 0xB6}, + {0x0, 0x00, 0xB7}, + {0x0, 0x00, 0xB8}, + {0x0, 0x00, 0xB9}, + {0x0, 0x00, 0xBA}, + {0x0, 0x00, 0xBB}, + {0x0, 0x00, 0xBC}, + {0x0, 0x00, 0xBD}, + {0x0, 0x00, 0xBE}, + {0x0, 0x00, 0xBF}, + {0x0, 0x00, 0xC0}, + {0x0, 0x00, 0xC1}, + {0x0, 0x00, 0xC2}, + {0x0, 0x00, 0xC3}, + {0x0, 0x00, 0xC4}, + {0x0, 0x00, 0xC5}, + {0x0, 0x00, 0xC6}, + {0x0, 0x00, 0xC7}, + {0x0, 0x00, 0xC8}, + {0x0, 0x00, 0xC9}, + {0x0, 0x00, 0xCA}, + {0x0, 0x00, 0xCB}, + {0x0, 0x00, 0xCC}, + {0x1, 0xF4, 0x00}, + {0x1, 0x38, 0x01}, + {0x1, 0x40, 0x02}, + {0x1, 0x0A, 0x03}, + {0x1, 0x40, 0x04}, + {0x1, 0x40, 0x05}, + {0x1, 0x40, 0x06}, + {0x1, 0x67, 0x07}, + {0x1, 0x31, 0x08}, + {0x1, 0x00, 0x09}, + {0x1, 0x00, 0x0A}, + {0x1, 0x00, 0x0B}, + {0x1, 0x14, 0x0C}, + {0x1, 0x00, 0x0D}, + {0x1, 0x00, 0x0E}, + {0x1, 0x00, 0x0F}, + {0x1, 0x1E, 0x10}, + {0x1, 0x00, 0x11}, + {0x1, 0x00, 0x12}, + {0x1, 0x00, 0x13}, + {0x1, 0x00, 0x14}, + {0x1, 0xFF, 0x15}, + {0x1, 0x01, 0x16}, + {0x1, 0x32, 0x17}, + {0x1, 0x23, 0x18}, + {0x1, 0xCE, 0x19}, + {0x1, 0x23, 0x1A}, + {0x1, 0x32, 0x1B}, + {0x1, 0x8D, 0x1C}, + {0x1, 0xCE, 0x1D}, + {0x1, 0x8D, 0x1E}, + {0x1, 0x00, 0x1F}, + {0x1, 0x00, 0x20}, + {0x1, 0xFF, 0x3E}, + {0x1, 0x02, 0x3F}, + {0x1, 0x00, 0x40}, + {0x1, 0x00, 0x41}, + {0x1, 0x00, 0x42}, + {0x1, 0x00, 0x43}, + {0x1, 0x00, 0x44}, + {0x1, 0x00, 0x45}, + {0x1, 0x00, 0x46}, + {0x1, 0x00, 0x47}, + {0x1, 0x00, 0x48}, + {0x1, 0x00, 0x49}, + {0x1, 0x00, 0x4A}, + {0x1, 0x00, 0x4B}, + {0x1, 0x00, 0x4C}, + {0x1, 0x00, 0x4D}, + {0x1, 0x00, 0x4E}, + {0x1, 0x00, 0x4F}, + {0x1, 0x00, 0x50}, + {0x1, 0x00, 0x51}, + {0x1, 0x00, 0x52}, + {0x1, 0x00, 0x53}, + {0x1, 0x00, 0x54}, + {0x1, 0x00, 0x55}, + {0x1, 0x00, 0x56}, + {0x1, 0x00, 0x57}, + {0x1, 0x00, 0x58}, + {0x1, 0x00, 0x59}, + {0x1, 0x00, 0x5A}, + {0x2, 0x03, 0x00}, + {0x2, 0x00, 0x01}, + {0x2, 0x00, 0x05}, + {0x2, 0x00, 0x06}, + {0x2, 0x00, 0x07}, + {0x2, 0x00, 0x10}, + {0x2, 0x00, 0x11}, + /* Strange - looks like the 501 driver doesn't do anything + * at insert time except read the EEPROM + */ + {} +}; + +/* Data for video camera init before capture. + * Capture and decoding by Colin Peart. + * This is is for the 3com HomeConnect Lite which is spca501a based. + */ +static __u16 spca501_3com_open_data[][3] = { + /* bmRequest,value,index */ + {0x2, 0x0050, 0x0000}, /* C/S Enable TG soft reset, timing mode=010 */ + {0x2, 0x0043, 0x0000}, /* C/S Disable TG soft reset, timing mode=010 */ + {0x2, 0x0002, 0x0005}, /* C/S GPIO */ + {0x2, 0x0003, 0x0005}, /* C/S GPIO */ + +#ifdef CCDSP_SET + {0x1, 0x0020, 0x0001}, /* CCDSP Options */ + + {0x1, 0x0020, 0x0002}, /* CCDSP Black Level */ + {0x1, 0x006e, 0x0007}, /* CCDSP Gamma options */ + {0x1, 0x0090, 0x0015}, /* CCDSP Luminance Low */ + {0x1, 0x00ff, 0x0016}, /* CCDSP Luminance High */ + {0x1, 0x0003, 0x003F}, /* CCDSP Gamma correction toggle */ + +#ifdef ALTER_GAMMA + {0x1, 0x0010, 0x0008}, /* CCDSP YUV A11 */ + {0x1, 0x0000, 0x0009}, /* CCDSP YUV A12 */ + {0x1, 0x0000, 0x000a}, /* CCDSP YUV A13 */ + {0x1, 0x0000, 0x000b}, /* CCDSP YUV A21 */ + {0x1, 0x0010, 0x000c}, /* CCDSP YUV A22 */ + {0x1, 0x0000, 0x000d}, /* CCDSP YUV A23 */ + {0x1, 0x0000, 0x000e}, /* CCDSP YUV A31 */ + {0x1, 0x0000, 0x000f}, /* CCDSP YUV A32 */ + {0x1, 0x0010, 0x0010}, /* CCDSP YUV A33 */ + {0x1, 0x0000, 0x0011}, /* CCDSP R Offset */ + {0x1, 0x0000, 0x0012}, /* CCDSP G Offset */ + {0x1, 0x0001, 0x0013}, /* CCDSP B Offset */ + {0x1, 0x0001, 0x0014}, /* CCDSP BG Offset */ + {0x1, 0x003f, 0x00C1}, /* CCDSP Gamma Correction Enable */ +#endif +#endif + +#ifdef TG_SET + {0x0, 0x00fc, 0x0000}, /* TG Shutter Speed High Bits */ + {0x0, 0x0000, 0x0001}, /* TG Shutter Speed Low Bits */ + {0x0, 0x00e4, 0x0004}, /* TG DCLK*2 Adjust */ + {0x0, 0x0008, 0x0005}, /* TG ADCK Adjust */ + {0x0, 0x0003, 0x0006}, /* TG FR Phase Adjust */ + {0x0, 0x0001, 0x0007}, /* TG FCDS Phase Adjust */ + {0x0, 0x0039, 0x0008}, /* TG FS Phase Adjust */ + {0x0, 0x0088, 0x000a}, /* TG MH1 */ + {0x0, 0x0003, 0x000f}, /* TG Pixel ID */ + + /* Like below, unexplained toglleing */ + {0x0, 0x0080, 0x000c}, + {0x0, 0x0000, 0x000d}, + {0x0, 0x0080, 0x000c}, + {0x0, 0x0004, 0x000d}, + {0x0, 0x0000, 0x000c}, + {0x0, 0x0000, 0x000d}, + {0x0, 0x0040, 0x000c}, + {0x0, 0x0017, 0x000d}, + {0x0, 0x00c0, 0x000c}, + {0x0, 0x0000, 0x000d}, + {0x0, 0x0080, 0x000c}, + {0x0, 0x0006, 0x000d}, + {0x0, 0x0080, 0x000c}, + {0x0, 0x0004, 0x000d}, + {0x0, 0x0002, 0x0003}, +#endif + +#ifdef DSPWIN_SET + {0x1, 0x001c, 0x0017}, /* CCDSP W1 Start X */ + {0x1, 0x00e2, 0x0019}, /* CCDSP W2 Start X */ + {0x1, 0x001c, 0x001b}, /* CCDSP W3 Start X */ + {0x1, 0x00e2, 0x001d}, /* CCDSP W4 Start X */ + {0x1, 0x00aa, 0x001f}, /* CCDSP W5 Start X */ + {0x1, 0x0070, 0x0020}, /* CCDSP W5 Start Y */ +#endif + {0x0, 0x0001, 0x0010}, /* TG Start Clock */ + +/* {0x2, 0x006a, 0x0001}, * C/S Enable ISOSYNCH Packet Engine */ + {0x2, 0x0068, 0x0001}, /* C/S Diable ISOSYNCH Packet Engine */ + {0x2, 0x0000, 0x0005}, + {0x2, 0x0043, 0x0000}, /* C/S Set Timing Mode, Disable TG soft reset */ + {0x2, 0x0043, 0x0000}, /* C/S Set Timing Mode, Disable TG soft reset */ + {0x2, 0x0002, 0x0005}, /* C/S GPIO */ + {0x2, 0x0003, 0x0005}, /* C/S GPIO */ + + {0x2, 0x006a, 0x0001}, /* C/S Enable ISOSYNCH Packet Engine */ + {} +}; + +/* + * Data used to initialize a SPCA501C with HV7131B sensor. + * From a capture file taken with USBSnoop v 1.5 + * I have a "SPCA501C pc camera chipset" manual by sunplus, but some + * of the value meanings are obscure or simply "reserved". + * to do list: + * 1) Understand what every value means + * 2) Understand why some values seem to appear more than once + * 3) Write a small comment for each line of the following arrays. + */ +static __u16 spca501c_arowana_open_data[][3] = { + /* bmRequest,value,index */ + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x01, 0x0006, 0x0011}, + {0x01, 0x00ff, 0x0012}, + {0x01, 0x0014, 0x0013}, + {0x01, 0x0000, 0x0014}, + {0x01, 0x0042, 0x0051}, + {0x01, 0x0040, 0x0052}, + {0x01, 0x0051, 0x0053}, + {0x01, 0x0040, 0x0054}, + {0x01, 0x0000, 0x0055}, + {0x00, 0x0025, 0x0000}, + {0x00, 0x0026, 0x0000}, + {0x00, 0x0001, 0x0000}, + {0x00, 0x0027, 0x0000}, + {0x00, 0x008a, 0x0000}, + {} +}; + +static __u16 spca501c_arowana_init_data[][3] = { + /* bmRequest,value,index */ + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x01, 0x0006, 0x0011}, + {0x01, 0x00ff, 0x0012}, + {0x01, 0x0014, 0x0013}, + {0x01, 0x0000, 0x0014}, + {0x01, 0x0042, 0x0051}, + {0x01, 0x0040, 0x0052}, + {0x01, 0x0051, 0x0053}, + {0x01, 0x0040, 0x0054}, + {0x01, 0x0000, 0x0055}, + {0x00, 0x0025, 0x0000}, + {0x00, 0x0026, 0x0000}, + {0x00, 0x0001, 0x0000}, + {0x00, 0x0027, 0x0000}, + {0x00, 0x008a, 0x0000}, + {0x02, 0x0000, 0x0005}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x2000, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0015, 0x0001}, + {0x05, 0x00ea, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0023, 0x0001}, + {0x05, 0x0003, 0x0000}, + {0x05, 0x0030, 0x0001}, + {0x05, 0x002b, 0x0000}, + {0x05, 0x0031, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0032, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0033, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0034, 0x0001}, + {0x05, 0x0002, 0x0000}, + {0x05, 0x0050, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0051, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0052, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0054, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x00, 0x0000, 0x0001}, + {0x00, 0x0000, 0x0002}, + {0x00, 0x000c, 0x0003}, + {0x00, 0x0000, 0x0004}, + {0x00, 0x0090, 0x0005}, + {0x00, 0x0000, 0x0006}, + {0x00, 0x0040, 0x0007}, + {0x00, 0x00c0, 0x0008}, + {0x00, 0x004a, 0x0009}, + {0x00, 0x0000, 0x000a}, + {0x00, 0x0000, 0x000b}, + {0x00, 0x0001, 0x000c}, + {0x00, 0x0001, 0x000d}, + {0x00, 0x0000, 0x000e}, + {0x00, 0x0002, 0x000f}, + {0x00, 0x0001, 0x0010}, + {0x00, 0x0000, 0x0011}, + {0x00, 0x0000, 0x0012}, + {0x00, 0x0002, 0x0020}, + {0x00, 0x0080, 0x0021}, + {0x00, 0x0001, 0x0022}, + {0x00, 0x00e0, 0x0023}, + {0x00, 0x0000, 0x0024}, + {0x00, 0x00d5, 0x0025}, + {0x00, 0x0000, 0x0026}, + {0x00, 0x000b, 0x0027}, + {0x00, 0x0000, 0x0046}, + {0x00, 0x0000, 0x0047}, + {0x00, 0x0000, 0x0048}, + {0x00, 0x0000, 0x0049}, + {0x00, 0x0008, 0x004a}, + {0xff, 0x0000, 0x00d0}, + {0xff, 0x00d8, 0x00d1}, + {0xff, 0x0000, 0x00d4}, + {0xff, 0x0000, 0x00d5}, + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0028, 0x0001}, + {0x01, 0x0000, 0x0002}, + {0x01, 0x000a, 0x0003}, + {0x01, 0x0040, 0x0004}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0x00fd, 0x000a}, + {0x01, 0x0038, 0x000b}, + {0x01, 0x00d1, 0x000c}, + {0x01, 0x00f7, 0x000d}, + {0x01, 0x00ed, 0x000e}, + {0x01, 0x00d8, 0x000f}, + {0x01, 0x0038, 0x0010}, + {0x01, 0x00ff, 0x0015}, + {0x01, 0x0001, 0x0016}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x0000, 0x001f}, + {0x01, 0x0000, 0x0020}, + {0x01, 0x00ff, 0x003e}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0000, 0x0040}, + {0x01, 0x0035, 0x0041}, + {0x01, 0x0053, 0x0042}, + {0x01, 0x0069, 0x0043}, + {0x01, 0x007c, 0x0044}, + {0x01, 0x008c, 0x0045}, + {0x01, 0x009a, 0x0046}, + {0x01, 0x00a8, 0x0047}, + {0x01, 0x00b4, 0x0048}, + {0x01, 0x00bf, 0x0049}, + {0x01, 0x00ca, 0x004a}, + {0x01, 0x00d4, 0x004b}, + {0x01, 0x00dd, 0x004c}, + {0x01, 0x00e7, 0x004d}, + {0x01, 0x00ef, 0x004e}, + {0x01, 0x00f8, 0x004f}, + {0x01, 0x00ff, 0x0050}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0060, 0x0057}, + {0x01, 0x0040, 0x0058}, + {0x01, 0x0011, 0x0059}, + {0x01, 0x0001, 0x005a}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x0015, 0x0006}, + {0x02, 0x100a, 0x0007}, + {0x02, 0xa048, 0x0000}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0025, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0001, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0020, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x00, 0x0090, 0x0005}, + {0x01, 0x00a6, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x2000, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0015, 0x0001}, + {0x05, 0x00ea, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0023, 0x0001}, + {0x05, 0x0003, 0x0000}, + {0x05, 0x0030, 0x0001}, + {0x05, 0x002b, 0x0000}, + {0x05, 0x0031, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0032, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0033, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0034, 0x0001}, + {0x05, 0x0002, 0x0000}, + {0x05, 0x0050, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0051, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0052, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0054, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x00, 0x0000, 0x0001}, + {0x00, 0x0000, 0x0002}, + {0x00, 0x000c, 0x0003}, + {0x00, 0x0000, 0x0004}, + {0x00, 0x0090, 0x0005}, + {0x00, 0x0000, 0x0006}, + {0x00, 0x0040, 0x0007}, + {0x00, 0x00c0, 0x0008}, + {0x00, 0x004a, 0x0009}, + {0x00, 0x0000, 0x000a}, + {0x00, 0x0000, 0x000b}, + {0x00, 0x0001, 0x000c}, + {0x00, 0x0001, 0x000d}, + {0x00, 0x0000, 0x000e}, + {0x00, 0x0002, 0x000f}, + {0x00, 0x0001, 0x0010}, + {0x00, 0x0000, 0x0011}, + {0x00, 0x0000, 0x0012}, + {0x00, 0x0002, 0x0020}, + {0x00, 0x0080, 0x0021}, + {0x00, 0x0001, 0x0022}, + {0x00, 0x00e0, 0x0023}, + {0x00, 0x0000, 0x0024}, + {0x00, 0x00d5, 0x0025}, + {0x00, 0x0000, 0x0026}, + {0x00, 0x000b, 0x0027}, + {0x00, 0x0000, 0x0046}, + {0x00, 0x0000, 0x0047}, + {0x00, 0x0000, 0x0048}, + {0x00, 0x0000, 0x0049}, + {0x00, 0x0008, 0x004a}, + {0xff, 0x0000, 0x00d0}, + {0xff, 0x00d8, 0x00d1}, + {0xff, 0x0000, 0x00d4}, + {0xff, 0x0000, 0x00d5}, + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0028, 0x0001}, + {0x01, 0x0000, 0x0002}, + {0x01, 0x000a, 0x0003}, + {0x01, 0x0040, 0x0004}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0x00fd, 0x000a}, + {0x01, 0x0038, 0x000b}, + {0x01, 0x00d1, 0x000c}, + {0x01, 0x00f7, 0x000d}, + {0x01, 0x00ed, 0x000e}, + {0x01, 0x00d8, 0x000f}, + {0x01, 0x0038, 0x0010}, + {0x01, 0x00ff, 0x0015}, + {0x01, 0x0001, 0x0016}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x0000, 0x001f}, + {0x01, 0x0000, 0x0020}, + {0x01, 0x00ff, 0x003e}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0000, 0x0040}, + {0x01, 0x0035, 0x0041}, + {0x01, 0x0053, 0x0042}, + {0x01, 0x0069, 0x0043}, + {0x01, 0x007c, 0x0044}, + {0x01, 0x008c, 0x0045}, + {0x01, 0x009a, 0x0046}, + {0x01, 0x00a8, 0x0047}, + {0x01, 0x00b4, 0x0048}, + {0x01, 0x00bf, 0x0049}, + {0x01, 0x00ca, 0x004a}, + {0x01, 0x00d4, 0x004b}, + {0x01, 0x00dd, 0x004c}, + {0x01, 0x00e7, 0x004d}, + {0x01, 0x00ef, 0x004e}, + {0x01, 0x00f8, 0x004f}, + {0x01, 0x00ff, 0x0050}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0060, 0x0057}, + {0x01, 0x0040, 0x0058}, + {0x01, 0x0011, 0x0059}, + {0x01, 0x0001, 0x005a}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x0015, 0x0006}, + {0x02, 0x100a, 0x0007}, + {0x02, 0xa048, 0x0000}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0025, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0001, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0020, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x00, 0x0090, 0x0005}, + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0xfffd, 0x000a}, + {0x01, 0x0023, 0x000b}, + {0x01, 0xffea, 0x000c}, + {0x01, 0xfff4, 0x000d}, + {0x01, 0xfffc, 0x000e}, + {0x01, 0xffe3, 0x000f}, + {0x01, 0x001f, 0x0010}, + {0x01, 0x00a8, 0x0001}, + {0x01, 0x0067, 0x0007}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x00c8, 0x0015}, + {0x01, 0x0032, 0x0016}, + {0x01, 0x0000, 0x0011}, + {0x01, 0x0000, 0x0012}, + {0x01, 0x0000, 0x0013}, + {0x01, 0x000a, 0x0003}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xc000, 0x0001}, + {0x02, 0x0000, 0x0005}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x2000, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0015, 0x0001}, + {0x05, 0x00ea, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0023, 0x0001}, + {0x05, 0x0003, 0x0000}, + {0x05, 0x0030, 0x0001}, + {0x05, 0x002b, 0x0000}, + {0x05, 0x0031, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0032, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0033, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0034, 0x0001}, + {0x05, 0x0002, 0x0000}, + {0x05, 0x0050, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0051, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0052, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0054, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x00, 0x0000, 0x0001}, + {0x00, 0x0000, 0x0002}, + {0x00, 0x000c, 0x0003}, + {0x00, 0x0000, 0x0004}, + {0x00, 0x0090, 0x0005}, + {0x00, 0x0000, 0x0006}, + {0x00, 0x0040, 0x0007}, + {0x00, 0x00c0, 0x0008}, + {0x00, 0x004a, 0x0009}, + {0x00, 0x0000, 0x000a}, + {0x00, 0x0000, 0x000b}, + {0x00, 0x0001, 0x000c}, + {0x00, 0x0001, 0x000d}, + {0x00, 0x0000, 0x000e}, + {0x00, 0x0002, 0x000f}, + {0x00, 0x0001, 0x0010}, + {0x00, 0x0000, 0x0011}, + {0x00, 0x0000, 0x0012}, + {0x00, 0x0002, 0x0020}, + {0x00, 0x0080, 0x0021}, + {0x00, 0x0001, 0x0022}, + {0x00, 0x00e0, 0x0023}, + {0x00, 0x0000, 0x0024}, + {0x00, 0x00d5, 0x0025}, + {0x00, 0x0000, 0x0026}, + {0x00, 0x000b, 0x0027}, + {0x00, 0x0000, 0x0046}, + {0x00, 0x0000, 0x0047}, + {0x00, 0x0000, 0x0048}, + {0x00, 0x0000, 0x0049}, + {0x00, 0x0008, 0x004a}, + {0xff, 0x0000, 0x00d0}, + {0xff, 0x00d8, 0x00d1}, + {0xff, 0x0000, 0x00d4}, + {0xff, 0x0000, 0x00d5}, + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0028, 0x0001}, + {0x01, 0x0000, 0x0002}, + {0x01, 0x000a, 0x0003}, + {0x01, 0x0040, 0x0004}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0x00fd, 0x000a}, + {0x01, 0x0038, 0x000b}, + {0x01, 0x00d1, 0x000c}, + {0x01, 0x00f7, 0x000d}, + {0x01, 0x00ed, 0x000e}, + {0x01, 0x00d8, 0x000f}, + {0x01, 0x0038, 0x0010}, + {0x01, 0x00ff, 0x0015}, + {0x01, 0x0001, 0x0016}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x0000, 0x001f}, + {0x01, 0x0000, 0x0020}, + {0x01, 0x00ff, 0x003e}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0000, 0x0040}, + {0x01, 0x0035, 0x0041}, + {0x01, 0x0053, 0x0042}, + {0x01, 0x0069, 0x0043}, + {0x01, 0x007c, 0x0044}, + {0x01, 0x008c, 0x0045}, + {0x01, 0x009a, 0x0046}, + {0x01, 0x00a8, 0x0047}, + {0x01, 0x00b4, 0x0048}, + {0x01, 0x00bf, 0x0049}, + {0x01, 0x00ca, 0x004a}, + {0x01, 0x00d4, 0x004b}, + {0x01, 0x00dd, 0x004c}, + {0x01, 0x00e7, 0x004d}, + {0x01, 0x00ef, 0x004e}, + {0x01, 0x00f8, 0x004f}, + {0x01, 0x00ff, 0x0050}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0060, 0x0057}, + {0x01, 0x0040, 0x0058}, + {0x01, 0x0011, 0x0059}, + {0x01, 0x0001, 0x005a}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x0015, 0x0006}, + {0x02, 0x100a, 0x0007}, + {0x02, 0xa048, 0x0000}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0025, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0001, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0020, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x00, 0x0090, 0x0005}, + {0x01, 0x00a6, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x2000, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0015, 0x0001}, + {0x05, 0x00ea, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0023, 0x0001}, + {0x05, 0x0003, 0x0000}, + {0x05, 0x0030, 0x0001}, + {0x05, 0x002b, 0x0000}, + {0x05, 0x0031, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0032, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0033, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0034, 0x0001}, + {0x05, 0x0002, 0x0000}, + {0x05, 0x0050, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0051, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0052, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0054, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x00, 0x0000, 0x0001}, + {0x00, 0x0000, 0x0002}, + {0x00, 0x000c, 0x0003}, + {0x00, 0x0000, 0x0004}, + {0x00, 0x0090, 0x0005}, + {0x00, 0x0000, 0x0006}, + {0x00, 0x0040, 0x0007}, + {0x00, 0x00c0, 0x0008}, + {0x00, 0x004a, 0x0009}, + {0x00, 0x0000, 0x000a}, + {0x00, 0x0000, 0x000b}, + {0x00, 0x0001, 0x000c}, + {0x00, 0x0001, 0x000d}, + {0x00, 0x0000, 0x000e}, + {0x00, 0x0002, 0x000f}, + {0x00, 0x0001, 0x0010}, + {0x00, 0x0000, 0x0011}, + {0x00, 0x0000, 0x0012}, + {0x00, 0x0002, 0x0020}, + {0x00, 0x0080, 0x0021}, + {0x00, 0x0001, 0x0022}, + {0x00, 0x00e0, 0x0023}, + {0x00, 0x0000, 0x0024}, + {0x00, 0x00d5, 0x0025}, + {0x00, 0x0000, 0x0026}, + {0x00, 0x000b, 0x0027}, + {0x00, 0x0000, 0x0046}, + {0x00, 0x0000, 0x0047}, + {0x00, 0x0000, 0x0048}, + {0x00, 0x0000, 0x0049}, + {0x00, 0x0008, 0x004a}, + {0xff, 0x0000, 0x00d0}, + {0xff, 0x00d8, 0x00d1}, + {0xff, 0x0000, 0x00d4}, + {0xff, 0x0000, 0x00d5}, + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0028, 0x0001}, + {0x01, 0x0000, 0x0002}, + {0x01, 0x000a, 0x0003}, + {0x01, 0x0040, 0x0004}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0x00fd, 0x000a}, + {0x01, 0x0038, 0x000b}, + {0x01, 0x00d1, 0x000c}, + {0x01, 0x00f7, 0x000d}, + {0x01, 0x00ed, 0x000e}, + {0x01, 0x00d8, 0x000f}, + {0x01, 0x0038, 0x0010}, + {0x01, 0x00ff, 0x0015}, + {0x01, 0x0001, 0x0016}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x0000, 0x001f}, + {0x01, 0x0000, 0x0020}, + {0x01, 0x00ff, 0x003e}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0000, 0x0040}, + {0x01, 0x0035, 0x0041}, + {0x01, 0x0053, 0x0042}, + {0x01, 0x0069, 0x0043}, + {0x01, 0x007c, 0x0044}, + {0x01, 0x008c, 0x0045}, + {0x01, 0x009a, 0x0046}, + {0x01, 0x00a8, 0x0047}, + {0x01, 0x00b4, 0x0048}, + {0x01, 0x00bf, 0x0049}, + {0x01, 0x00ca, 0x004a}, + {0x01, 0x00d4, 0x004b}, + {0x01, 0x00dd, 0x004c}, + {0x01, 0x00e7, 0x004d}, + {0x01, 0x00ef, 0x004e}, + {0x01, 0x00f8, 0x004f}, + {0x01, 0x00ff, 0x0050}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0060, 0x0057}, + {0x01, 0x0040, 0x0058}, + {0x01, 0x0011, 0x0059}, + {0x01, 0x0001, 0x005a}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x0015, 0x0006}, + {0x02, 0x100a, 0x0007}, + {0x02, 0xa048, 0x0000}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0025, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0001, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0020, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x00, 0x0090, 0x0005}, + {0x01, 0x00a6, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x000f, 0x0000}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0xfffd, 0x000a}, + {0x01, 0x0023, 0x000b}, + {0x01, 0xffea, 0x000c}, + {0x01, 0xfff4, 0x000d}, + {0x01, 0xfffc, 0x000e}, + {0x01, 0xffe3, 0x000f}, + {0x01, 0x001f, 0x0010}, + {0x01, 0x00a8, 0x0001}, + {0x01, 0x0067, 0x0007}, + {0x01, 0x0042, 0x0051}, + {0x01, 0x0051, 0x0053}, + {0x01, 0x000a, 0x0003}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xc000, 0x0001}, + {0x02, 0x0000, 0x0005}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x2000, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0015, 0x0001}, + {0x05, 0x00ea, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0023, 0x0001}, + {0x05, 0x0003, 0x0000}, + {0x05, 0x0030, 0x0001}, + {0x05, 0x002b, 0x0000}, + {0x05, 0x0031, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0032, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0033, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0034, 0x0001}, + {0x05, 0x0002, 0x0000}, + {0x05, 0x0050, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0051, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0052, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0054, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x00, 0x0000, 0x0001}, + {0x00, 0x0000, 0x0002}, + {0x00, 0x000c, 0x0003}, + {0x00, 0x0000, 0x0004}, + {0x00, 0x0090, 0x0005}, + {0x00, 0x0000, 0x0006}, + {0x00, 0x0040, 0x0007}, + {0x00, 0x00c0, 0x0008}, + {0x00, 0x004a, 0x0009}, + {0x00, 0x0000, 0x000a}, + {0x00, 0x0000, 0x000b}, + {0x00, 0x0001, 0x000c}, + {0x00, 0x0001, 0x000d}, + {0x00, 0x0000, 0x000e}, + {0x00, 0x0002, 0x000f}, + {0x00, 0x0001, 0x0010}, + {0x00, 0x0000, 0x0011}, + {0x00, 0x0000, 0x0012}, + {0x00, 0x0002, 0x0020}, + {0x00, 0x0080, 0x0021}, + {0x00, 0x0001, 0x0022}, + {0x00, 0x00e0, 0x0023}, + {0x00, 0x0000, 0x0024}, + {0x00, 0x00d5, 0x0025}, + {0x00, 0x0000, 0x0026}, + {0x00, 0x000b, 0x0027}, + {0x00, 0x0000, 0x0046}, + {0x00, 0x0000, 0x0047}, + {0x00, 0x0000, 0x0048}, + {0x00, 0x0000, 0x0049}, + {0x00, 0x0008, 0x004a}, + {0xff, 0x0000, 0x00d0}, + {0xff, 0x00d8, 0x00d1}, + {0xff, 0x0000, 0x00d4}, + {0xff, 0x0000, 0x00d5}, + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0028, 0x0001}, + {0x01, 0x0000, 0x0002}, + {0x01, 0x000a, 0x0003}, + {0x01, 0x0040, 0x0004}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0x00fd, 0x000a}, + {0x01, 0x0038, 0x000b}, + {0x01, 0x00d1, 0x000c}, + {0x01, 0x00f7, 0x000d}, + {0x01, 0x00ed, 0x000e}, + {0x01, 0x00d8, 0x000f}, + {0x01, 0x0038, 0x0010}, + {0x01, 0x00ff, 0x0015}, + {0x01, 0x0001, 0x0016}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x0000, 0x001f}, + {0x01, 0x0000, 0x0020}, + {0x01, 0x00ff, 0x003e}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0000, 0x0040}, + {0x01, 0x0035, 0x0041}, + {0x01, 0x0053, 0x0042}, + {0x01, 0x0069, 0x0043}, + {0x01, 0x007c, 0x0044}, + {0x01, 0x008c, 0x0045}, + {0x01, 0x009a, 0x0046}, + {0x01, 0x00a8, 0x0047}, + {0x01, 0x00b4, 0x0048}, + {0x01, 0x00bf, 0x0049}, + {0x01, 0x00ca, 0x004a}, + {0x01, 0x00d4, 0x004b}, + {0x01, 0x00dd, 0x004c}, + {0x01, 0x00e7, 0x004d}, + {0x01, 0x00ef, 0x004e}, + {0x01, 0x00f8, 0x004f}, + {0x01, 0x00ff, 0x0050}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0060, 0x0057}, + {0x01, 0x0040, 0x0058}, + {0x01, 0x0011, 0x0059}, + {0x01, 0x0001, 0x005a}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x0015, 0x0006}, + {0x02, 0x100a, 0x0007}, + {0x02, 0xa048, 0x0000}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0025, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0001, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0020, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x00, 0x0090, 0x0005}, + {0x01, 0x00a6, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x2000, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0015, 0x0001}, + {0x05, 0x00ea, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0023, 0x0001}, + {0x05, 0x0003, 0x0000}, + {0x05, 0x0030, 0x0001}, + {0x05, 0x002b, 0x0000}, + {0x05, 0x0031, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0032, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0033, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0034, 0x0001}, + {0x05, 0x0002, 0x0000}, + {0x05, 0x0050, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0051, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0052, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0054, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x00, 0x0000, 0x0001}, + {0x00, 0x0000, 0x0002}, + {0x00, 0x000c, 0x0003}, + {0x00, 0x0000, 0x0004}, + {0x00, 0x0090, 0x0005}, + {0x00, 0x0000, 0x0006}, + {0x00, 0x0040, 0x0007}, + {0x00, 0x00c0, 0x0008}, + {0x00, 0x004a, 0x0009}, + {0x00, 0x0000, 0x000a}, + {0x00, 0x0000, 0x000b}, + {0x00, 0x0001, 0x000c}, + {0x00, 0x0001, 0x000d}, + {0x00, 0x0000, 0x000e}, + {0x00, 0x0002, 0x000f}, + {0x00, 0x0001, 0x0010}, + {0x00, 0x0000, 0x0011}, + {0x00, 0x0000, 0x0012}, + {0x00, 0x0002, 0x0020}, + {0x00, 0x0080, 0x0021}, + {0x00, 0x0001, 0x0022}, + {0x00, 0x00e0, 0x0023}, + {0x00, 0x0000, 0x0024}, + {0x00, 0x00d5, 0x0025}, + {0x00, 0x0000, 0x0026}, + {0x00, 0x000b, 0x0027}, + {0x00, 0x0000, 0x0046}, + {0x00, 0x0000, 0x0047}, + {0x00, 0x0000, 0x0048}, + {0x00, 0x0000, 0x0049}, + {0x00, 0x0008, 0x004a}, + {0xff, 0x0000, 0x00d0}, + {0xff, 0x00d8, 0x00d1}, + {0xff, 0x0000, 0x00d4}, + {0xff, 0x0000, 0x00d5}, + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0028, 0x0001}, + {0x01, 0x0000, 0x0002}, + {0x01, 0x000a, 0x0003}, + {0x01, 0x0040, 0x0004}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0x00fd, 0x000a}, + {0x01, 0x0038, 0x000b}, + {0x01, 0x00d1, 0x000c}, + {0x01, 0x00f7, 0x000d}, + {0x01, 0x00ed, 0x000e}, + {0x01, 0x00d8, 0x000f}, + {0x01, 0x0038, 0x0010}, + {0x01, 0x00ff, 0x0015}, + {0x01, 0x0001, 0x0016}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x0000, 0x001f}, + {0x01, 0x0000, 0x0020}, + {0x01, 0x00ff, 0x003e}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0000, 0x0040}, + {0x01, 0x0035, 0x0041}, + {0x01, 0x0053, 0x0042}, + {0x01, 0x0069, 0x0043}, + {0x01, 0x007c, 0x0044}, + {0x01, 0x008c, 0x0045}, + {0x01, 0x009a, 0x0046}, + {0x01, 0x00a8, 0x0047}, + {0x01, 0x00b4, 0x0048}, + {0x01, 0x00bf, 0x0049}, + {0x01, 0x00ca, 0x004a}, + {0x01, 0x00d4, 0x004b}, + {0x01, 0x00dd, 0x004c}, + {0x01, 0x00e7, 0x004d}, + {0x01, 0x00ef, 0x004e}, + {0x01, 0x00f8, 0x004f}, + {0x01, 0x00ff, 0x0050}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0060, 0x0057}, + {0x01, 0x0040, 0x0058}, + {0x01, 0x0011, 0x0059}, + {0x01, 0x0001, 0x005a}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x0015, 0x0006}, + {0x02, 0x100a, 0x0007}, + {0x02, 0xa048, 0x0000}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0025, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0001, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0020, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x00, 0x0090, 0x0005}, + {0x01, 0x00a6, 0x0000}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x001e, 0x0000}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x0011, 0x0008}, + {0x01, 0x0032, 0x0009}, + {0x01, 0xfffd, 0x000a}, + {0x01, 0x0023, 0x000b}, + {0x01, 0xffea, 0x000c}, + {0x01, 0xfff4, 0x000d}, + {0x01, 0xfffc, 0x000e}, + {0x01, 0xffe3, 0x000f}, + {0x01, 0x001f, 0x0010}, + {0x01, 0x00a8, 0x0001}, + {0x01, 0x0067, 0x0007}, + {0x01, 0x0042, 0x0051}, + {0x01, 0x0051, 0x0053}, + {0x01, 0x000a, 0x0003}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x0007, 0x0005}, + {0x01, 0x0042, 0x0051}, + {0x01, 0x0051, 0x0053}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x002d, 0x0000}, + {0x01, 0x0003, 0x003f}, + {0x01, 0x0001, 0x0056}, + {0x02, 0xc000, 0x0001}, + {0x02, 0x0000, 0x0005}, + {} +}; + +/* Unknow camera from Ori Usbid 0x0000:0x0000 */ +/* Based on snoops from Ori Cohen */ +static __u16 spca501c_mysterious_open_data[][3] = { + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, +/* DSP Registers */ + {0x01, 0x0016, 0x0011}, /* RGB offset */ + {0x01, 0x0000, 0x0012}, + {0x01, 0x0006, 0x0013}, + {0x01, 0x0078, 0x0051}, + {0x01, 0x0040, 0x0052}, + {0x01, 0x0046, 0x0053}, + {0x01, 0x0040, 0x0054}, + {0x00, 0x0025, 0x0000}, +/* {0x00, 0x0000, 0x0000 }, */ +/* Part 2 */ +/* TG Registers */ + {0x00, 0x0026, 0x0000}, + {0x00, 0x0001, 0x0000}, + {0x00, 0x0027, 0x0000}, + {0x00, 0x008a, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x2000, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0015, 0x0001}, + {0x05, 0x00ea, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0023, 0x0001}, + {0x05, 0x0003, 0x0000}, + {0x05, 0x0030, 0x0001}, + {0x05, 0x002b, 0x0000}, + {0x05, 0x0031, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0032, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0033, 0x0001}, + {0x05, 0x0023, 0x0000}, + {0x05, 0x0034, 0x0001}, + {0x05, 0x0002, 0x0000}, + {0x05, 0x0050, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0051, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0052, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0054, 0x0001}, + {0x05, 0x0001, 0x0000}, + {} +}; + +/* Based on snoops from Ori Cohen */ +static __u16 spca501c_mysterious_init_data[][3] = { +/* Part 3 */ +/* TG registers */ +/* {0x00, 0x0000, 0x0000}, */ + {0x00, 0x0000, 0x0001}, + {0x00, 0x0000, 0x0002}, + {0x00, 0x0006, 0x0003}, + {0x00, 0x0000, 0x0004}, + {0x00, 0x0090, 0x0005}, + {0x00, 0x0000, 0x0006}, + {0x00, 0x0040, 0x0007}, + {0x00, 0x00c0, 0x0008}, + {0x00, 0x004a, 0x0009}, + {0x00, 0x0000, 0x000a}, + {0x00, 0x0000, 0x000b}, + {0x00, 0x0001, 0x000c}, + {0x00, 0x0001, 0x000d}, + {0x00, 0x0000, 0x000e}, + {0x00, 0x0002, 0x000f}, + {0x00, 0x0001, 0x0010}, + {0x00, 0x0000, 0x0011}, + {0x00, 0x0001, 0x0012}, + {0x00, 0x0002, 0x0020}, + {0x00, 0x0080, 0x0021}, /* 640 */ + {0x00, 0x0001, 0x0022}, + {0x00, 0x00e0, 0x0023}, /* 480 */ + {0x00, 0x0000, 0x0024}, /* Offset H hight */ + {0x00, 0x00d3, 0x0025}, /* low */ + {0x00, 0x0000, 0x0026}, /* Offset V */ + {0x00, 0x000d, 0x0027}, /* low */ + {0x00, 0x0000, 0x0046}, + {0x00, 0x0000, 0x0047}, + {0x00, 0x0000, 0x0048}, + {0x00, 0x0000, 0x0049}, + {0x00, 0x0008, 0x004a}, +/* DSP Registers */ + {0x01, 0x00a6, 0x0000}, + {0x01, 0x0028, 0x0001}, + {0x01, 0x0000, 0x0002}, + {0x01, 0x000a, 0x0003}, /* Level Calc bit7 ->1 Auto */ + {0x01, 0x0040, 0x0004}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x000f, 0x0008}, /* A11 Color correction coeff */ + {0x01, 0x002d, 0x0009}, /* A12 */ + {0x01, 0x0005, 0x000a}, /* A13 */ + {0x01, 0x0023, 0x000b}, /* A21 */ + {0x01, 0x00e0, 0x000c}, /* A22 */ + {0x01, 0x00fd, 0x000d}, /* A23 */ + {0x01, 0x00f4, 0x000e}, /* A31 */ + {0x01, 0x00e4, 0x000f}, /* A32 */ + {0x01, 0x0028, 0x0010}, /* A33 */ + {0x01, 0x00ff, 0x0015}, /* Reserved */ + {0x01, 0x0001, 0x0016}, /* Reserved */ + {0x01, 0x0032, 0x0017}, /* Win1 Start begin */ + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x0000, 0x001f}, + {0x01, 0x0000, 0x0020}, /* Win1 Start end */ + {0x01, 0x00ff, 0x003e}, /* Reserved begin */ + {0x01, 0x0002, 0x003f}, + {0x01, 0x0000, 0x0040}, + {0x01, 0x0035, 0x0041}, + {0x01, 0x0053, 0x0042}, + {0x01, 0x0069, 0x0043}, + {0x01, 0x007c, 0x0044}, + {0x01, 0x008c, 0x0045}, + {0x01, 0x009a, 0x0046}, + {0x01, 0x00a8, 0x0047}, + {0x01, 0x00b4, 0x0048}, + {0x01, 0x00bf, 0x0049}, + {0x01, 0x00ca, 0x004a}, + {0x01, 0x00d4, 0x004b}, + {0x01, 0x00dd, 0x004c}, + {0x01, 0x00e7, 0x004d}, + {0x01, 0x00ef, 0x004e}, + {0x01, 0x00f8, 0x004f}, + {0x01, 0x00ff, 0x0050}, + {0x01, 0x0003, 0x0056}, /* Reserved end */ + {0x01, 0x0060, 0x0057}, /* Edge Gain */ + {0x01, 0x0040, 0x0058}, + {0x01, 0x0011, 0x0059}, /* Edge Bandwidth */ + {0x01, 0x0001, 0x005a}, + {0x02, 0x0007, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x02, 0x0007, 0x0005}, + {0x02, 0x0015, 0x0006}, + {0x02, 0x200a, 0x0007}, + {0x02, 0xa048, 0x0000}, + {0x02, 0xc000, 0x0001}, + {0x02, 0x000f, 0x0005}, + {0x02, 0xa048, 0x0000}, + {0x05, 0x0022, 0x0004}, + {0x05, 0x0025, 0x0001}, + {0x05, 0x0000, 0x0000}, +/* Part 4 */ + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0001, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x05, 0x0021, 0x0001}, + {0x05, 0x00d2, 0x0000}, + {0x05, 0x0020, 0x0001}, + {0x05, 0x0000, 0x0000}, + {0x00, 0x0090, 0x0005}, + {0x01, 0x00a6, 0x0000}, + {0x02, 0x0000, 0x0005}, + {0x05, 0x0026, 0x0001}, + {0x05, 0x0001, 0x0000}, + {0x05, 0x0027, 0x0001}, + {0x05, 0x004e, 0x0000}, +/* Part 5 */ + {0x01, 0x0003, 0x003f}, + {0x01, 0x0001, 0x0056}, + {0x01, 0x000f, 0x0008}, + {0x01, 0x002d, 0x0009}, + {0x01, 0x0005, 0x000a}, + {0x01, 0x0023, 0x000b}, + {0x01, 0xffe0, 0x000c}, + {0x01, 0xfffd, 0x000d}, + {0x01, 0xfff4, 0x000e}, + {0x01, 0xffe4, 0x000f}, + {0x01, 0x0028, 0x0010}, + {0x01, 0x00a8, 0x0001}, + {0x01, 0x0066, 0x0007}, + {0x01, 0x0032, 0x0017}, + {0x01, 0x0023, 0x0018}, + {0x01, 0x00ce, 0x0019}, + {0x01, 0x0023, 0x001a}, + {0x01, 0x0032, 0x001b}, + {0x01, 0x008d, 0x001c}, + {0x01, 0x00ce, 0x001d}, + {0x01, 0x008d, 0x001e}, + {0x01, 0x00c8, 0x0015}, /* c8 Poids fort Luma */ + {0x01, 0x0032, 0x0016}, /* 32 */ + {0x01, 0x0016, 0x0011}, /* R 00 */ + {0x01, 0x0016, 0x0012}, /* G 00 */ + {0x01, 0x0016, 0x0013}, /* B 00 */ + {0x01, 0x000a, 0x0003}, + {0x02, 0xc002, 0x0001}, + {0x02, 0x0007, 0x0005}, + {} +}; + +static int reg_write(struct usb_device *dev, + __u16 req, __u16 index, __u16 value) +{ + int ret; + + ret = usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + req, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + PDEBUG(D_USBO, "reg write: 0x%02x 0x%02x 0x%02x", + req, index, value); + if (ret < 0) + PDEBUG(D_ERR, "reg write: error %d", ret); + return ret; +} + +/* returns: negative is error, pos or zero is data */ +static int reg_read(struct usb_device *dev, + __u16 req, /* bRequest */ + __u16 index, /* wIndex */ + __u16 length) /* wLength (1 or 2 only) */ +{ + int ret; + __u8 buf[2]; + + buf[1] = 0; + ret = usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, + buf, length, + 500); /* timeout */ + if (ret < 0) { + PDEBUG(D_ERR, "reg_read err %d", ret); + return -1; + } + return (buf[1] << 8) + buf[0]; +} + +static int write_vector(struct gspca_dev *gspca_dev, + __u16 data[][3]) +{ + struct usb_device *dev = gspca_dev->dev; + int ret, i = 0; + + while (data[i][0] != 0 || data[i][1] != 0 || data[i][2] != 0) { + ret = reg_write(dev, data[i][0], data[i][2], data[i][1]); + if (ret < 0) { + PDEBUG(D_ERR, + "Reg write failed for 0x%02x,0x%02x,0x%02x", + data[i][0], data[i][1], data[i][2]); + return ret; + } + i++; + } + return 0; +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x11, sd->brightness); + reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x12, sd->brightness); + reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x13, sd->brightness); +} + +static void getbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u16 brightness; + + brightness = reg_read(gspca_dev->dev, SPCA501_REG_CCDSP, 0x11, 2); + sd->brightness = brightness << 1; +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_write(gspca_dev->dev, 0x00, 0x00, + (sd->contrast >> 8) & 0xff); + reg_write(gspca_dev->dev, 0x00, 0x01, + sd->contrast & 0xff); +} + +static void getcontrast(struct gspca_dev *gspca_dev) +{ +/* spca50x->contrast = 0xaa01; */ +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x0c, sd->colors); +} + +static void getcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = reg_read(gspca_dev->dev, SPCA501_REG_CCDSP, 0x0c, 2); +/* sd->hue = (reg_read(gspca_dev->dev, SPCA501_REG_CCDSP, 0x13, */ +/* 2) & 0xFF) << 8; */ +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + __u16 vendor; + __u16 product; + + vendor = id->idVendor; + product = id->idProduct; + switch (vendor) { + case 0x0000: /* Unknow Camera */ +/* switch (product) { */ +/* case 0x0000: */ + sd->subtype = MystFromOriUnknownCamera; +/* break; */ +/* } */ + break; + case 0x040a: /* Kodak cameras */ +/* switch (product) { */ +/* case 0x0002: */ + sd->subtype = KodakDVC325; +/* break; */ +/* } */ + break; + case 0x0497: /* Smile International */ +/* switch (product) { */ +/* case 0xc001: */ + sd->subtype = SmileIntlCamera; +/* break; */ +/* } */ + break; + case 0x0506: /* 3COM cameras */ +/* switch (product) { */ +/* case 0x00df: */ + sd->subtype = ThreeComHomeConnectLite; +/* break; */ +/* } */ + break; + case 0x0733: /* Rebadged ViewQuest (Intel) and ViewQuest cameras */ + switch (product) { + case 0x0401: + sd->subtype = IntelCreateAndShare; + break; + case 0x0402: + sd->subtype = ViewQuestM318B; + break; + } + break; + case 0x1776: /* Arowana */ +/* switch (product) { */ +/* case 0x501c: */ + sd->subtype = Arowana300KCMOSCamera; +/* break; */ +/* } */ + break; + } + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + cam->cam_mode = vga_mode; + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + sd->brightness = sd_ctrls[MY_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[MY_CONTRAST].qctrl.default_value; + sd->colors = sd_ctrls[MY_COLOR].qctrl.default_value; + + switch (sd->subtype) { + case Arowana300KCMOSCamera: + case SmileIntlCamera: + /* Arowana 300k CMOS Camera data */ + if (write_vector(gspca_dev, spca501c_arowana_init_data)) + goto error; + break; + case MystFromOriUnknownCamera: + /* UnKnow Ori CMOS Camera data */ + if (write_vector(gspca_dev, spca501c_mysterious_open_data)) + goto error; + break; + default: + /* generic spca501 init data */ + if (write_vector(gspca_dev, spca501_init_data)) + goto error; + break; + } + return 0; +error: + return -EINVAL; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + PDEBUG(D_STREAM, "SPCA501 init"); + switch (sd->subtype) { + case ThreeComHomeConnectLite: + /* Special handling for 3com data */ + write_vector(gspca_dev, spca501_3com_open_data); + break; + case Arowana300KCMOSCamera: + case SmileIntlCamera: + /* Arowana 300k CMOS Camera data */ + write_vector(gspca_dev, spca501c_arowana_open_data); + break; + case MystFromOriUnknownCamera: + /* UnKnow CMOS Camera data */ + write_vector(gspca_dev, spca501c_mysterious_init_data); + break; + default: + /* Generic 501 open data */ + write_vector(gspca_dev, spca501_open_data); + } + PDEBUG(D_STREAM, "Initializing SPCA501 finished"); + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + int mode; + + /* memorize the wanted pixel format */ + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode; + + /* Enable ISO packet machine CTRL reg=2, + * index=1 bitmask=0x2 (bit ordinal 1) */ + reg_write(dev, SPCA50X_REG_USB, 0x6, 0x94); + switch (mode) { + case 0: /* 640x480 */ + reg_write(dev, SPCA50X_REG_USB, 0x07, 0x004a); + break; + case 1: /* 320x240 */ + reg_write(dev, SPCA50X_REG_USB, 0x07, 0x104a); + break; + default: +/* case 2: * 160x120 */ + reg_write(dev, SPCA50X_REG_USB, 0x07, 0x204a); + break; + } + reg_write(dev, SPCA501_REG_CTLRL, 0x01, 0x02); + + /* HDG atleast the Intel CreateAndShare needs to have one of its + * brightness / contrast / color set otherwise it assumes wath seems + * max contrast. Note that strange enough setting any of these is + * enough to fix the max contrast problem, to be sure we set all 3 */ + setbrightness(gspca_dev); + setcontrast(gspca_dev); + setcolors(gspca_dev); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + /* Disable ISO packet + * machine CTRL reg=2, index=1 bitmask=0x0 (bit ordinal 1) */ + reg_write(gspca_dev->dev, SPCA501_REG_CTLRL, 0x01, 0x00); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +/* this function is called at close time */ +static void sd_close(struct gspca_dev *gspca_dev) +{ + reg_write(gspca_dev->dev, SPCA501_REG_CTLRL, 0x05, 0x00); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + switch (data[0]) { + case 0: /* start of frame */ + frame = gspca_frame_add(gspca_dev, + LAST_PACKET, + frame, + data, 0); + data += SPCA501_OFFSET_DATA; + len -= SPCA501_OFFSET_DATA; + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + return; + case 0xff: /* drop */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + data++; + len--; + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + data, len); +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcontrast(gspca_dev); + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcolors(gspca_dev); + *val = sd->colors; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x040a, 0x0002), DVNM("Kodak DVC-325")}, + {USB_DEVICE(0x0497, 0xc001), DVNM("Smile International")}, + {USB_DEVICE(0x0506, 0x00df), DVNM("3Com HomeConnect Lite")}, + {USB_DEVICE(0x0733, 0x0401), DVNM("Intel Create and Share")}, + {USB_DEVICE(0x0733, 0x0402), DVNM("ViewQuest M318B")}, + {USB_DEVICE(0x1776, 0x501c), DVNM("Arowana 300K CMOS Camera")}, + {USB_DEVICE(0x0000, 0x0000), DVNM("MystFromOri Unknow Camera")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/video/gspca/spca505.c new file mode 100644 index 000000000000..5b23518d970d --- /dev/null +++ b/drivers/media/video/gspca/spca505.c @@ -0,0 +1,933 @@ +/* + * SPCA505 chip based cameras initialization data + * + * V4L2 by Jean-Francis Moine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define MODULE_NAME "spca505" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/SPCA505 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + int buflen; + unsigned char tmpbuf[640 * 480 * 3 / 2]; /* YYUV per line */ + unsigned char tmpbuf2[640 * 480 * 2]; /* YUYV */ + + unsigned char brightness; + + char subtype; +#define IntelPCCameraPro 0 +#define Nxultra 1 +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 127, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +}; + +static struct cam_mode vga_mode[] = { + {V4L2_PIX_FMT_YUYV, 160, 120, 5}, + {V4L2_PIX_FMT_YUYV, 176, 144, 4}, + {V4L2_PIX_FMT_YUYV, 320, 240, 2}, + {V4L2_PIX_FMT_YUYV, 352, 288, 1}, + {V4L2_PIX_FMT_YUYV, 640, 480, 0}, +}; + +#define SPCA50X_OFFSET_DATA 10 + +#define SPCA50X_REG_USB 0x02 /* spca505 501 */ + +#define SPCA50X_USB_CTRL 0x00 /* spca505 */ +#define SPCA50X_CUSB_ENABLE 0x01 /* spca505 */ +#define SPCA50X_REG_GLOBAL 0x03 /* spca505 */ +#define SPCA50X_GMISC0_IDSEL 0x01 /* Global control device ID select spca505 */ +#define SPCA50X_GLOBAL_MISC0 0x00 /* Global control miscellaneous 0 spca505 */ + +#define SPCA50X_GLOBAL_MISC1 0x01 /* 505 */ +#define SPCA50X_GLOBAL_MISC3 0x03 /* 505 */ +#define SPCA50X_GMISC3_SAA7113RST 0x20 /* Not sure about this one spca505 */ + +/* + * Data to initialize a SPCA505. Common to the CCD and external modes + */ +static __u16 spca505_init_data[][3] = { + /* line bmRequest,value,index */ + /* 1819 */ + {SPCA50X_REG_GLOBAL, SPCA50X_GMISC3_SAA7113RST, SPCA50X_GLOBAL_MISC3}, + /* Sensor reset */ + /* 1822 */ {SPCA50X_REG_GLOBAL, 0x00, SPCA50X_GLOBAL_MISC3}, + /* 1825 */ {SPCA50X_REG_GLOBAL, 0x00, SPCA50X_GLOBAL_MISC1}, + /* Block USB reset */ + /* 1828 */ {SPCA50X_REG_GLOBAL, SPCA50X_GMISC0_IDSEL, + SPCA50X_GLOBAL_MISC0}, + + /* 1831 */ {0x5, 0x01, 0x10}, + /* Maybe power down some stuff */ + /* 1834 */ {0x5, 0x0f, 0x11}, + + /* Setup internal CCD ? */ + /* 1837 */ {0x6, 0x10, 0x08}, + /* 1840 */ {0x6, 0x00, 0x09}, + /* 1843 */ {0x6, 0x00, 0x0a}, + /* 1846 */ {0x6, 0x00, 0x0b}, + /* 1849 */ {0x6, 0x10, 0x0c}, + /* 1852 */ {0x6, 0x00, 0x0d}, + /* 1855 */ {0x6, 0x00, 0x0e}, + /* 1858 */ {0x6, 0x00, 0x0f}, + /* 1861 */ {0x6, 0x10, 0x10}, + /* 1864 */ {0x6, 0x02, 0x11}, + /* 1867 */ {0x6, 0x00, 0x12}, + /* 1870 */ {0x6, 0x04, 0x13}, + /* 1873 */ {0x6, 0x02, 0x14}, + /* 1876 */ {0x6, 0x8a, 0x51}, + /* 1879 */ {0x6, 0x40, 0x52}, + /* 1882 */ {0x6, 0xb6, 0x53}, + /* 1885 */ {0x6, 0x3d, 0x54}, + {} +}; + +/* + * Data to initialize the camera using the internal CCD + */ +static __u16 spca505_open_data_ccd[][3] = { + /* line bmRequest,value,index */ + /* Internal CCD data set */ + /* 1891 */ {0x3, 0x04, 0x01}, + /* This could be a reset */ + /* 1894 */ {0x3, 0x00, 0x01}, + + /* Setup compression and image registers. 0x6 and 0x7 seem to be + related to H&V hold, and are resolution mode specific */ + /* 1897 */ {0x4, 0x10, 0x01}, + /* DIFF(0x50), was (0x10) */ + /* 1900 */ {0x4, 0x00, 0x04}, + /* 1903 */ {0x4, 0x00, 0x05}, + /* 1906 */ {0x4, 0x20, 0x06}, + /* 1909 */ {0x4, 0x20, 0x07}, + + /* 1912 */ {0x8, 0x0a, 0x00}, + /* DIFF (0x4a), was (0xa) */ + + /* 1915 */ {0x5, 0x00, 0x10}, + /* 1918 */ {0x5, 0x00, 0x11}, + /* 1921 */ {0x5, 0x00, 0x00}, + /* DIFF not written */ + /* 1924 */ {0x5, 0x00, 0x01}, + /* DIFF not written */ + /* 1927 */ {0x5, 0x00, 0x02}, + /* DIFF not written */ + /* 1930 */ {0x5, 0x00, 0x03}, + /* DIFF not written */ + /* 1933 */ {0x5, 0x00, 0x04}, + /* DIFF not written */ + /* 1936 */ {0x5, 0x80, 0x05}, + /* DIFF not written */ + /* 1939 */ {0x5, 0xe0, 0x06}, + /* DIFF not written */ + /* 1942 */ {0x5, 0x20, 0x07}, + /* DIFF not written */ + /* 1945 */ {0x5, 0xa0, 0x08}, + /* DIFF not written */ + /* 1948 */ {0x5, 0x0, 0x12}, + /* DIFF not written */ + /* 1951 */ {0x5, 0x02, 0x0f}, + /* DIFF not written */ + /* 1954 */ {0x5, 0x10, 0x46}, + /* DIFF not written */ + /* 1957 */ {0x5, 0x8, 0x4a}, + /* DIFF not written */ + + /* 1960 */ {0x3, 0x08, 0x03}, + /* DIFF (0x3,0x28,0x3) */ + /* 1963 */ {0x3, 0x08, 0x01}, + /* 1966 */ {0x3, 0x0c, 0x03}, + /* DIFF not written */ + /* 1969 */ {0x3, 0x21, 0x00}, + /* DIFF (0x39) */ + +/* Extra block copied from init to hopefully ensure CCD is in a sane state */ + /* 1837 */ {0x6, 0x10, 0x08}, + /* 1840 */ {0x6, 0x00, 0x09}, + /* 1843 */ {0x6, 0x00, 0x0a}, + /* 1846 */ {0x6, 0x00, 0x0b}, + /* 1849 */ {0x6, 0x10, 0x0c}, + /* 1852 */ {0x6, 0x00, 0x0d}, + /* 1855 */ {0x6, 0x00, 0x0e}, + /* 1858 */ {0x6, 0x00, 0x0f}, + /* 1861 */ {0x6, 0x10, 0x10}, + /* 1864 */ {0x6, 0x02, 0x11}, + /* 1867 */ {0x6, 0x00, 0x12}, + /* 1870 */ {0x6, 0x04, 0x13}, + /* 1873 */ {0x6, 0x02, 0x14}, + /* 1876 */ {0x6, 0x8a, 0x51}, + /* 1879 */ {0x6, 0x40, 0x52}, + /* 1882 */ {0x6, 0xb6, 0x53}, + /* 1885 */ {0x6, 0x3d, 0x54}, + /* End of extra block */ + + /* 1972 */ {0x6, 0x3f, 0x1}, + /* Block skipped */ + /* 1975 */ {0x6, 0x10, 0x02}, + /* 1978 */ {0x6, 0x64, 0x07}, + /* 1981 */ {0x6, 0x10, 0x08}, + /* 1984 */ {0x6, 0x00, 0x09}, + /* 1987 */ {0x6, 0x00, 0x0a}, + /* 1990 */ {0x6, 0x00, 0x0b}, + /* 1993 */ {0x6, 0x10, 0x0c}, + /* 1996 */ {0x6, 0x00, 0x0d}, + /* 1999 */ {0x6, 0x00, 0x0e}, + /* 2002 */ {0x6, 0x00, 0x0f}, + /* 2005 */ {0x6, 0x10, 0x10}, + /* 2008 */ {0x6, 0x02, 0x11}, + /* 2011 */ {0x6, 0x00, 0x12}, + /* 2014 */ {0x6, 0x04, 0x13}, + /* 2017 */ {0x6, 0x02, 0x14}, + /* 2020 */ {0x6, 0x8a, 0x51}, + /* 2023 */ {0x6, 0x40, 0x52}, + /* 2026 */ {0x6, 0xb6, 0x53}, + /* 2029 */ {0x6, 0x3d, 0x54}, + /* 2032 */ {0x6, 0x60, 0x57}, + /* 2035 */ {0x6, 0x20, 0x58}, + /* 2038 */ {0x6, 0x15, 0x59}, + /* 2041 */ {0x6, 0x05, 0x5a}, + + /* 2044 */ {0x5, 0x01, 0xc0}, + /* 2047 */ {0x5, 0x10, 0xcb}, + /* 2050 */ {0x5, 0x80, 0xc1}, + /* */ + /* 2053 */ {0x5, 0x0, 0xc2}, + /* 4 was 0 */ + /* 2056 */ {0x5, 0x00, 0xca}, + /* 2059 */ {0x5, 0x80, 0xc1}, + /* */ + /* 2062 */ {0x5, 0x04, 0xc2}, + /* 2065 */ {0x5, 0x00, 0xca}, + /* 2068 */ {0x5, 0x0, 0xc1}, + /* */ + /* 2071 */ {0x5, 0x00, 0xc2}, + /* 2074 */ {0x5, 0x00, 0xca}, + /* 2077 */ {0x5, 0x40, 0xc1}, + /* */ + /* 2080 */ {0x5, 0x17, 0xc2}, + /* 2083 */ {0x5, 0x00, 0xca}, + /* 2086 */ {0x5, 0x80, 0xc1}, + /* */ + /* 2089 */ {0x5, 0x06, 0xc2}, + /* 2092 */ {0x5, 0x00, 0xca}, + /* 2095 */ {0x5, 0x80, 0xc1}, + /* */ + /* 2098 */ {0x5, 0x04, 0xc2}, + /* 2101 */ {0x5, 0x00, 0xca}, + + /* 2104 */ {0x3, 0x4c, 0x3}, + /* 2107 */ {0x3, 0x18, 0x1}, + + /* 2110 */ {0x6, 0x70, 0x51}, + /* 2113 */ {0x6, 0xbe, 0x53}, + /* 2116 */ {0x6, 0x71, 0x57}, + /* 2119 */ {0x6, 0x20, 0x58}, + /* 2122 */ {0x6, 0x05, 0x59}, + /* 2125 */ {0x6, 0x15, 0x5a}, + + /* 2128 */ {0x4, 0x00, 0x08}, + /* Compress = OFF (0x1 to turn on) */ + /* 2131 */ {0x4, 0x12, 0x09}, + /* 2134 */ {0x4, 0x21, 0x0a}, + /* 2137 */ {0x4, 0x10, 0x0b}, + /* 2140 */ {0x4, 0x21, 0x0c}, + /* 2143 */ {0x4, 0x05, 0x00}, + /* was 5 (Image Type ? ) */ + /* 2146 */ {0x4, 0x00, 0x01}, + + /* 2149 */ {0x6, 0x3f, 0x01}, + + /* 2152 */ {0x4, 0x00, 0x04}, + /* 2155 */ {0x4, 0x00, 0x05}, + /* 2158 */ {0x4, 0x40, 0x06}, + /* 2161 */ {0x4, 0x40, 0x07}, + + /* 2164 */ {0x6, 0x1c, 0x17}, + /* 2167 */ {0x6, 0xe2, 0x19}, + /* 2170 */ {0x6, 0x1c, 0x1b}, + /* 2173 */ {0x6, 0xe2, 0x1d}, + /* 2176 */ {0x6, 0xaa, 0x1f}, + /* 2179 */ {0x6, 0x70, 0x20}, + + /* 2182 */ {0x5, 0x01, 0x10}, + /* 2185 */ {0x5, 0x00, 0x11}, + /* 2188 */ {0x5, 0x01, 0x00}, + /* 2191 */ {0x5, 0x05, 0x01}, + /* 2194 */ {0x5, 0x00, 0xc1}, + /* */ + /* 2197 */ {0x5, 0x00, 0xc2}, + /* 2200 */ {0x5, 0x00, 0xca}, + + /* 2203 */ {0x6, 0x70, 0x51}, + /* 2206 */ {0x6, 0xbe, 0x53}, + {} +}; + +/* + Made by Tomasz Zablocki (skalamandra@poczta.onet.pl) + * SPCA505b chip based cameras initialization data + * + */ +/* jfm */ +#define initial_brightness 0x7f /* 0x0(white)-0xff(black) */ +/* #define initial_brightness 0x0 //0x0(white)-0xff(black) */ +/* + * Data to initialize a SPCA505. Common to the CCD and external modes + */ +static __u16 spca505b_init_data[][3] = { +/* start */ + {0x02, 0x00, 0x00}, /* init */ + {0x02, 0x00, 0x01}, + {0x02, 0x00, 0x02}, + {0x02, 0x00, 0x03}, + {0x02, 0x00, 0x04}, + {0x02, 0x00, 0x05}, + {0x02, 0x00, 0x06}, + {0x02, 0x00, 0x07}, + {0x02, 0x00, 0x08}, + {0x02, 0x00, 0x09}, + {0x03, 0x00, 0x00}, + {0x03, 0x00, 0x01}, + {0x03, 0x00, 0x02}, + {0x03, 0x00, 0x03}, + {0x03, 0x00, 0x04}, + {0x03, 0x00, 0x05}, + {0x03, 0x00, 0x06}, + {0x04, 0x00, 0x00}, + {0x04, 0x00, 0x02}, + {0x04, 0x00, 0x04}, + {0x04, 0x00, 0x05}, + {0x04, 0x00, 0x06}, + {0x04, 0x00, 0x07}, + {0x04, 0x00, 0x08}, + {0x04, 0x00, 0x09}, + {0x04, 0x00, 0x0a}, + {0x04, 0x00, 0x0b}, + {0x04, 0x00, 0x0c}, + {0x07, 0x00, 0x00}, + {0x07, 0x00, 0x03}, + {0x08, 0x00, 0x00}, + {0x08, 0x00, 0x01}, + {0x08, 0x00, 0x02}, + {0x00, 0x01, 0x00}, + {0x00, 0x01, 0x01}, + {0x00, 0x01, 0x34}, + {0x00, 0x01, 0x35}, + {0x06, 0x18, 0x08}, + {0x06, 0xfc, 0x09}, + {0x06, 0xfc, 0x0a}, + {0x06, 0xfc, 0x0b}, + {0x06, 0x18, 0x0c}, + {0x06, 0xfc, 0x0d}, + {0x06, 0xfc, 0x0e}, + {0x06, 0xfc, 0x0f}, + {0x06, 0x18, 0x10}, + {0x06, 0xfe, 0x12}, + {0x06, 0x00, 0x11}, + {0x06, 0x00, 0x14}, + {0x06, 0x00, 0x13}, + {0x06, 0x28, 0x51}, + {0x06, 0xff, 0x53}, + {0x02, 0x00, 0x08}, + + {0x03, 0x00, 0x03}, + {0x03, 0x10, 0x03}, + {} +}; + +/* + * Data to initialize the camera using the internal CCD + */ +static __u16 spca505b_open_data_ccd[][3] = { + +/* {0x02,0x00,0x00}, */ + {0x03, 0x04, 0x01}, /* rst */ + {0x03, 0x00, 0x01}, + {0x03, 0x00, 0x00}, + {0x03, 0x21, 0x00}, + {0x03, 0x00, 0x04}, + {0x03, 0x00, 0x03}, + {0x03, 0x18, 0x03}, + {0x03, 0x08, 0x01}, + {0x03, 0x1c, 0x03}, + {0x03, 0x5c, 0x03}, + {0x03, 0x5c, 0x03}, + {0x03, 0x18, 0x01}, + +/* same as 505 */ + {0x04, 0x10, 0x01}, + {0x04, 0x00, 0x04}, + {0x04, 0x00, 0x05}, + {0x04, 0x20, 0x06}, + {0x04, 0x20, 0x07}, + + {0x08, 0x0a, 0x00}, + + {0x05, 0x00, 0x10}, + {0x05, 0x00, 0x11}, + {0x05, 0x00, 0x12}, + {0x05, 0x6f, 0x00}, + {0x05, initial_brightness >> 6, 0x00}, + {0x05, initial_brightness << 2, 0x01}, + {0x05, 0x00, 0x02}, + {0x05, 0x01, 0x03}, + {0x05, 0x00, 0x04}, + {0x05, 0x03, 0x05}, + {0x05, 0xe0, 0x06}, + {0x05, 0x20, 0x07}, + {0x05, 0xa0, 0x08}, + {0x05, 0x00, 0x12}, + {0x05, 0x02, 0x0f}, + {0x05, 128, 0x14}, /* max exposure off (0=on) */ + {0x05, 0x01, 0xb0}, + {0x05, 0x01, 0xbf}, + {0x03, 0x02, 0x06}, + {0x05, 0x10, 0x46}, + {0x05, 0x08, 0x4a}, + + {0x06, 0x00, 0x01}, + {0x06, 0x10, 0x02}, + {0x06, 0x64, 0x07}, + {0x06, 0x18, 0x08}, + {0x06, 0xfc, 0x09}, + {0x06, 0xfc, 0x0a}, + {0x06, 0xfc, 0x0b}, + {0x04, 0x00, 0x01}, + {0x06, 0x18, 0x0c}, + {0x06, 0xfc, 0x0d}, + {0x06, 0xfc, 0x0e}, + {0x06, 0xfc, 0x0f}, + {0x06, 0x11, 0x10}, /* contrast */ + {0x06, 0x00, 0x11}, + {0x06, 0xfe, 0x12}, + {0x06, 0x00, 0x13}, + {0x06, 0x00, 0x14}, + {0x06, 0x9d, 0x51}, + {0x06, 0x40, 0x52}, + {0x06, 0x7c, 0x53}, + {0x06, 0x40, 0x54}, + {0x06, 0x02, 0x57}, + {0x06, 0x03, 0x58}, + {0x06, 0x15, 0x59}, + {0x06, 0x05, 0x5a}, + {0x06, 0x03, 0x56}, + {0x06, 0x02, 0x3f}, + {0x06, 0x00, 0x40}, + {0x06, 0x39, 0x41}, + {0x06, 0x69, 0x42}, + {0x06, 0x87, 0x43}, + {0x06, 0x9e, 0x44}, + {0x06, 0xb1, 0x45}, + {0x06, 0xbf, 0x46}, + {0x06, 0xcc, 0x47}, + {0x06, 0xd5, 0x48}, + {0x06, 0xdd, 0x49}, + {0x06, 0xe3, 0x4a}, + {0x06, 0xe8, 0x4b}, + {0x06, 0xed, 0x4c}, + {0x06, 0xf2, 0x4d}, + {0x06, 0xf7, 0x4e}, + {0x06, 0xfc, 0x4f}, + {0x06, 0xff, 0x50}, + + {0x05, 0x01, 0xc0}, + {0x05, 0x10, 0xcb}, + {0x05, 0x40, 0xc1}, + {0x05, 0x04, 0xc2}, + {0x05, 0x00, 0xca}, + {0x05, 0x40, 0xc1}, + {0x05, 0x09, 0xc2}, + {0x05, 0x00, 0xca}, + {0x05, 0xc0, 0xc1}, + {0x05, 0x09, 0xc2}, + {0x05, 0x00, 0xca}, + {0x05, 0x40, 0xc1}, + {0x05, 0x59, 0xc2}, + {0x05, 0x00, 0xca}, + {0x04, 0x00, 0x01}, + {0x05, 0x80, 0xc1}, + {0x05, 0xec, 0xc2}, + {0x05, 0x0, 0xca}, + + {0x06, 0x02, 0x57}, + {0x06, 0x01, 0x58}, + {0x06, 0x15, 0x59}, + {0x06, 0x0a, 0x5a}, + {0x06, 0x01, 0x57}, + {0x06, 0x8a, 0x03}, + {0x06, 0x0a, 0x6c}, + {0x06, 0x30, 0x01}, + {0x06, 0x20, 0x02}, + {0x06, 0x00, 0x03}, + + {0x05, 0x8c, 0x25}, + + {0x06, 0x4d, 0x51}, /* maybe saturation (4d) */ + {0x06, 0x84, 0x53}, /* making green (84) */ + {0x06, 0x00, 0x57}, /* sharpness (1) */ + {0x06, 0x18, 0x08}, + {0x06, 0xfc, 0x09}, + {0x06, 0xfc, 0x0a}, + {0x06, 0xfc, 0x0b}, + {0x06, 0x18, 0x0c}, /* maybe hue (18) */ + {0x06, 0xfc, 0x0d}, + {0x06, 0xfc, 0x0e}, + {0x06, 0xfc, 0x0f}, + {0x06, 0x18, 0x10}, /* maybe contrast (18) */ + + {0x05, 0x01, 0x02}, + + {0x04, 0x00, 0x08}, /* compression */ + {0x04, 0x12, 0x09}, + {0x04, 0x21, 0x0a}, + {0x04, 0x10, 0x0b}, + {0x04, 0x21, 0x0c}, + {0x04, 0x1d, 0x00}, /* imagetype (1d) */ + {0x04, 0x41, 0x01}, /* hardware snapcontrol */ + + {0x04, 0x00, 0x04}, + {0x04, 0x00, 0x05}, + {0x04, 0x10, 0x06}, + {0x04, 0x10, 0x07}, + {0x04, 0x40, 0x06}, + {0x04, 0x40, 0x07}, + {0x04, 0x00, 0x04}, + {0x04, 0x00, 0x05}, + + {0x06, 0x1c, 0x17}, + {0x06, 0xe2, 0x19}, + {0x06, 0x1c, 0x1b}, + {0x06, 0xe2, 0x1d}, + {0x06, 0x5f, 0x1f}, + {0x06, 0x32, 0x20}, + + {0x05, initial_brightness >> 6, 0x00}, + {0x05, initial_brightness << 2, 0x01}, + {0x05, 0x06, 0xc1}, + {0x05, 0x58, 0xc2}, + {0x05, 0x0, 0xca}, + {0x05, 0x0, 0x11}, + {} +}; + +static int reg_write(struct usb_device *dev, + __u16 reg, __u16 index, __u16 value) +{ + int ret; + + ret = usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + reg, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + PDEBUG(D_PACK, "reg write: 0x%02x,0x%02x:0x%02x, 0x%x", + reg, index, value, ret); + if (ret < 0) + PDEBUG(D_ERR, "reg write: error %d", ret); + return ret; +} + +/* returns: negative is error, pos or zero is data */ +static int reg_read(struct usb_device *dev, + __u16 reg, /* bRequest */ + __u16 index, /* wIndex */ + __u16 length) /* wLength (1 or 2 only) */ +{ + int ret; + unsigned char buf[4]; + + buf[1] = 0; + ret = usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + reg, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + (__u16) 0, /* value */ + (__u16) index, + buf, + length, + 500); /* timeout */ + if (ret < 0) { + PDEBUG(D_ERR, "reg_read err %d", ret); + return -1; + } + return (buf[1] << 8) + buf[0]; +} + +static int write_vector(struct gspca_dev *gspca_dev, + __u16 data[][3]) +{ + struct usb_device *dev = gspca_dev->dev; + int ret, i = 0; + + while (data[i][0] != 0 || data[i][1] != 0 || data[i][2] != 0) { + ret = reg_write(dev, data[i][0], data[i][2], data[i][1]); + if (ret < 0) { + PDEBUG(D_ERR, + "Register write failed for 0x%x,0x%x,0x%x", + data[i][0], data[i][1], data[i][2]); + return ret; + } + i++; + } + return 0; +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + __u16 vendor; + __u16 product; + + vendor = id->idVendor; + product = id->idProduct; + switch (vendor) { + case 0x041e: /* Creative cameras */ +/* switch (product) { */ +/* case 0x401d: * here505b */ + sd->subtype = Nxultra; +/* break; */ +/* } */ + break; + case 0x0733: /* Rebadged ViewQuest (Intel) and ViewQuest cameras */ +/* switch (product) { */ +/* case 0x0430: */ +/* fixme: may be UsbGrabberPV321 BRIDGE_SPCA506 SENSOR_SAA7113 */ + sd->subtype = IntelPCCameraPro; +/* break; */ +/* } */ + break; + } + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + cam->cam_mode = vga_mode; + if (sd->subtype != IntelPCCameraPro) + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + else /* no 640x480 for IntelPCCameraPro */ + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0] - 1; + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + + if (sd->subtype == Nxultra) { + if (write_vector(gspca_dev, spca505b_init_data)) + return -EIO; + } else { + if (write_vector(gspca_dev, spca505_init_data)) + return -EIO; + } + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + PDEBUG(D_STREAM, "Initializing SPCA505"); + if (sd->subtype == Nxultra) + write_vector(gspca_dev, spca505b_open_data_ccd); + else + write_vector(gspca_dev, spca505_open_data_ccd); + ret = reg_read(gspca_dev->dev, 6, 0x16, 2); + + if (ret < 0) { + PDEBUG(D_ERR|D_STREAM, + "register read failed for after vector read err = %d", + ret); + return -EIO; + } + PDEBUG(D_STREAM, + "After vector read returns : 0x%x should be 0x0101", + ret & 0xffff); + + ret = reg_write(gspca_dev->dev, 6, 0x16, 0x0a); + if (ret < 0) { + PDEBUG(D_ERR, "register write failed for (6,0xa,0x16) err=%d", + ret); + return -EIO; + } + reg_write(gspca_dev->dev, 5, 0xc2, 18); + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + int ret; + + /* necessary because without it we can see stream + * only once after loading module */ + /* stopping usb registers Tomasz change */ + reg_write(dev, 0x02, 0x0, 0x0); + switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode) { + case 0: + reg_write(dev, 0x04, 0x00, 0x00); + reg_write(dev, 0x04, 0x06, 0x10); + reg_write(dev, 0x04, 0x07, 0x10); + break; + case 1: + reg_write(dev, 0x04, 0x00, 0x01); + reg_write(dev, 0x04, 0x06, 0x1a); + reg_write(dev, 0x04, 0x07, 0x1a); + break; + case 2: + reg_write(dev, 0x04, 0x00, 0x02); + reg_write(dev, 0x04, 0x06, 0x1c); + reg_write(dev, 0x04, 0x07, 0x1d); + break; + case 4: + reg_write(dev, 0x04, 0x00, 0x04); + reg_write(dev, 0x04, 0x06, 0x34); + reg_write(dev, 0x04, 0x07, 0x34); + break; + default: +/* case 5: */ + reg_write(dev, 0x04, 0x00, 0x05); + reg_write(dev, 0x04, 0x06, 0x40); + reg_write(dev, 0x04, 0x07, 0x40); + break; + } +/* Enable ISO packet machine - should we do this here or in ISOC init ? */ + ret = reg_write(dev, SPCA50X_REG_USB, + SPCA50X_USB_CTRL, + SPCA50X_CUSB_ENABLE); + +/* reg_write(dev, 0x5, 0x0, 0x0); */ +/* reg_write(dev, 0x5, 0x0, 0x1); */ +/* reg_write(dev, 0x5, 0x11, 0x2); */ +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + /* Disable ISO packet machine */ + reg_write(gspca_dev->dev, 0x02, 0x00, 0x00); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +/* this function is called at close time */ +static void sd_close(struct gspca_dev *gspca_dev) +{ + /* This maybe reset or power control */ + reg_write(gspca_dev->dev, 0x03, 0x03, 0x20); + reg_write(gspca_dev->dev, 0x03, 0x01, 0x0); + reg_write(gspca_dev->dev, 0x03, 0x00, 0x1); + reg_write(gspca_dev->dev, 0x05, 0x10, 0x1); + reg_write(gspca_dev->dev, 0x05, 0x11, 0xf); +} + +/* convert YYUV per line to YUYV (YUV 4:2:2) */ +static void yyuv_decode(unsigned char *out, + unsigned char *in, + int width, + int height) +{ + unsigned char *Ui, *Vi, *yi, *yi1; + unsigned char *out1; + int i, j; + + yi = in; + for (i = height / 2; --i >= 0; ) { + out1 = out + width * 2; /* next line */ + yi1 = yi + width; + Ui = yi1 + width; + Vi = Ui + width / 2; + for (j = width / 2; --j >= 0; ) { + *out++ = 128 + *yi++; + *out++ = 128 + *Ui; + *out++ = 128 + *yi++; + *out++ = 128 + *Vi; + + *out1++ = 128 + *yi1++; + *out1++ = 128 + *Ui++; + *out1++ = 128 + *yi1++; + *out1++ = 128 + *Vi++; + } + yi += width * 2; + out = out1; + } +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + unsigned char *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (data[0]) { + case 0: /* start of frame */ + if (gspca_dev->last_packet_type == FIRST_PACKET) { + yyuv_decode(sd->tmpbuf2, sd->tmpbuf, + gspca_dev->width, + gspca_dev->height); + frame = gspca_frame_add(gspca_dev, + LAST_PACKET, + frame, + sd->tmpbuf2, + gspca_dev->width + * gspca_dev->height + * 2); + } + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, 0); + data += SPCA50X_OFFSET_DATA; + len -= SPCA50X_OFFSET_DATA; + if (len > 0) + memcpy(sd->tmpbuf, data, len); + else + len = 0; + sd->buflen = len; + return; + case 0xff: /* drop */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + data += 1; + len -= 1; + memcpy(&sd->tmpbuf[sd->buflen], data, len); + sd->buflen += len; +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + __u8 brightness = sd->brightness; + reg_write(gspca_dev->dev, 5, 0x00, (255 - brightness) >> 6); + reg_write(gspca_dev->dev, 5, 0x01, (255 - brightness) << 2); + +} +static void getbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = 255 + - ((reg_read(gspca_dev->dev, 5, 0x01, 1) >> 2) + + (reg_read(gspca_dev->dev, 5, 0x0, 1) << 6)); +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x041e, 0x401d), DVNM("Creative Webcam NX ULTRA")}, + {USB_DEVICE(0x0733, 0x0430), DVNM("Intel PC Camera Pro")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof (struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/spca506.c b/drivers/media/video/gspca/spca506.c new file mode 100644 index 000000000000..614fb3ad7711 --- /dev/null +++ b/drivers/media/video/gspca/spca506.c @@ -0,0 +1,830 @@ +/* + * SPCA506 chip based cameras function + * M Xhaard 15/04/2004 based on different work Mark Taylor and others + * and my own snoopy file on a pv-321c donate by a german compagny + * "Firma Frank Gmbh" from Saarbruecken + * + * V4L2 by Jean-Francois Moine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "spca506" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/SPCA506 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + int buflen; + unsigned char tmpbuf[640 * 480 * 3]; /* YYUV per line */ + unsigned char tmpbuf2[640 * 480 * 2]; /* YUYV */ + + unsigned char brightness; + unsigned char contrast; + unsigned char colors; + unsigned char hue; + char norme; + char channel; +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val); +static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x80, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x47, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define SD_COLOR 2 + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x40, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, +#define SD_HUE 3 + { + { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0, + }, + .set = sd_sethue, + .get = sd_gethue, + }, +}; + +static struct cam_mode vga_mode[] = { + {V4L2_PIX_FMT_YUYV, 160, 120, 5}, + {V4L2_PIX_FMT_YUYV, 176, 144, 4}, + {V4L2_PIX_FMT_YUYV, 320, 240, 2}, + {V4L2_PIX_FMT_YUYV, 352, 288, 1}, + {V4L2_PIX_FMT_YUYV, 640, 480, 0}, +}; + +#define SPCA50X_OFFSET_DATA 10 + +#define SAA7113_bright 0x0a /* defaults 0x80 */ +#define SAA7113_contrast 0x0b /* defaults 0x47 */ +#define SAA7113_saturation 0x0c /* defaults 0x40 */ +#define SAA7113_hue 0x0d /* defaults 0x00 */ +#define SAA7113_I2C_BASE_WRITE 0x4a + +static void reg_r(struct usb_device *dev, + __u16 req, + __u16 index, + __u8 *buffer, __u16 length) +{ + usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, buffer, length, + 500); +} + +static void reg_w(struct usb_device *dev, + __u16 req, + __u16 value, + __u16 index) +{ + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, + NULL, 0, 500); +} + +static void spca506_Initi2c(struct gspca_dev *gspca_dev) +{ + reg_w(gspca_dev->dev, 0x07, SAA7113_I2C_BASE_WRITE, 0x0004); +} + +static void spca506_WriteI2c(struct gspca_dev *gspca_dev, __u16 valeur, + __u16 reg) +{ + int retry = 60; + unsigned char Data[2]; + + reg_w(gspca_dev->dev, 0x07, reg, 0x0001); + reg_w(gspca_dev->dev, 0x07, valeur, 0x0000); + while (retry--) { + reg_r(gspca_dev->dev, 0x07, 0x0003, Data, 2); + if ((Data[0] | Data[1]) == 0x00) + break; + } +} + +static int spca506_ReadI2c(struct gspca_dev *gspca_dev, __u16 reg) +{ + int retry = 60; + unsigned char Data[2]; + unsigned char value; + + reg_w(gspca_dev->dev, 0x07, SAA7113_I2C_BASE_WRITE, 0x0004); + reg_w(gspca_dev->dev, 0x07, reg, 0x0001); + reg_w(gspca_dev->dev, 0x07, 0x01, 0x0002); + while (--retry) { + reg_r(gspca_dev->dev, 0x07, 0x0003, Data, 2); + if ((Data[0] | Data[1]) == 0x00) + break; + } + if (retry == 0) + return -1; + reg_r(gspca_dev->dev, 0x07, 0x0000, &value, 1); + return value; +} + +static void spca506_SetNormeInput(struct gspca_dev *gspca_dev, + __u16 norme, + __u16 channel) +{ + struct sd *sd = (struct sd *) gspca_dev; +/* fixme: check if channel == 0..3 and 6..9 (8 values) */ + __u8 setbit0 = 0x00; + __u8 setbit1 = 0x00; + __u8 videomask = 0x00; + + PDEBUG(D_STREAM, "** Open Set Norme **"); + spca506_Initi2c(gspca_dev); + /* NTSC bit0 -> 1(525 l) PAL SECAM bit0 -> 0 (625 l) */ + /* Composite channel bit1 -> 1 S-video bit 1 -> 0 */ + /* and exclude SAA7113 reserved channel set default 0 otherwise */ + if (norme & V4L2_STD_NTSC) + setbit0 = 0x01; + if (channel == 4 || channel == 5 || channel > 9) + channel = 0; + if (channel < 4) + setbit1 = 0x02; + videomask = (0x48 | setbit0 | setbit1); + reg_w(gspca_dev->dev, 0x08, videomask, 0x0000); + spca506_WriteI2c(gspca_dev, (0xc0 | (channel & 0x0F)), 0x02); + + if (norme & V4L2_STD_NTSC) + spca506_WriteI2c(gspca_dev, 0x33, 0x0e); + /* Chrominance Control NTSC N */ + else if (norme & V4L2_STD_SECAM) + spca506_WriteI2c(gspca_dev, 0x53, 0x0e); + /* Chrominance Control SECAM */ + else + spca506_WriteI2c(gspca_dev, 0x03, 0x0e); + /* Chrominance Control PAL BGHIV */ + + sd->norme = norme; + sd->channel = channel; + PDEBUG(D_STREAM, "Set Video Byte to 0x%2x", videomask); + PDEBUG(D_STREAM, "Set Norme: %08x Channel %d", norme, channel); +} + +static void spca506_GetNormeInput(struct gspca_dev *gspca_dev, + __u16 *norme, __u16 *channel) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* Read the register is not so good value change so + we use your own copy in spca50x struct */ + *norme = sd->norme; + *channel = sd->channel; + PDEBUG(D_STREAM, "Get Norme: %d Channel %d", *norme, *channel); +} + +static void spca506_Setsize(struct gspca_dev *gspca_dev, __u16 code, + __u16 xmult, __u16 ymult) +{ + struct usb_device *dev = gspca_dev->dev; + + PDEBUG(D_STREAM, "** SetSize **"); + reg_w(dev, 0x04, (0x18 | (code & 0x07)), 0x0000); + /* Soft snap 0x40 Hard 0x41 */ + reg_w(dev, 0x04, 0x41, 0x0001); + reg_w(dev, 0x04, 0x00, 0x0002); + /* reserved */ + reg_w(dev, 0x04, 0x00, 0x0003); + + /* reserved */ + reg_w(dev, 0x04, 0x00, 0x0004); + /* reserved */ + reg_w(dev, 0x04, 0x01, 0x0005); + /* reserced */ + reg_w(dev, 0x04, xmult, 0x0006); + /* reserved */ + reg_w(dev, 0x04, ymult, 0x0007); + /* compression 1 */ + reg_w(dev, 0x04, 0x00, 0x0008); + /* T=64 -> 2 */ + reg_w(dev, 0x04, 0x00, 0x0009); + /* threshold2D */ + reg_w(dev, 0x04, 0x21, 0x000a); + /* quantization */ + reg_w(dev, 0x04, 0x00, 0x000b); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + cam->cam_mode = vga_mode; + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; + sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value; + sd->hue = sd_ctrls[SD_HUE].qctrl.default_value; + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + + reg_w(dev, 0x03, 0x00, 0x0004); + reg_w(dev, 0x03, 0xFF, 0x0003); + reg_w(dev, 0x03, 0x00, 0x0000); + reg_w(dev, 0x03, 0x1c, 0x0001); + reg_w(dev, 0x03, 0x18, 0x0001); + /* Init on PAL and composite input0 */ + spca506_SetNormeInput(gspca_dev, 0, 0); + reg_w(dev, 0x03, 0x1c, 0x0001); + reg_w(dev, 0x03, 0x18, 0x0001); + reg_w(dev, 0x05, 0x00, 0x0000); + reg_w(dev, 0x05, 0xef, 0x0001); + reg_w(dev, 0x05, 0x00, 0x00c1); + reg_w(dev, 0x05, 0x00, 0x00c2); + reg_w(dev, 0x06, 0x18, 0x0002); + reg_w(dev, 0x06, 0xf5, 0x0011); + reg_w(dev, 0x06, 0x02, 0x0012); + reg_w(dev, 0x06, 0xfb, 0x0013); + reg_w(dev, 0x06, 0x00, 0x0014); + reg_w(dev, 0x06, 0xa4, 0x0051); + reg_w(dev, 0x06, 0x40, 0x0052); + reg_w(dev, 0x06, 0x71, 0x0053); + reg_w(dev, 0x06, 0x40, 0x0054); + /************************************************/ + reg_w(dev, 0x03, 0x00, 0x0004); + reg_w(dev, 0x03, 0x00, 0x0003); + reg_w(dev, 0x03, 0x00, 0x0004); + reg_w(dev, 0x03, 0xFF, 0x0003); + reg_w(dev, 0x02, 0x00, 0x0000); + reg_w(dev, 0x03, 0x60, 0x0000); + reg_w(dev, 0x03, 0x18, 0x0001); + /* for a better reading mx :) */ + /*sdca506_WriteI2c(value,register) */ + spca506_Initi2c(gspca_dev); + spca506_WriteI2c(gspca_dev, 0x08, 0x01); + spca506_WriteI2c(gspca_dev, 0xc0, 0x02); + /* input composite video */ + spca506_WriteI2c(gspca_dev, 0x33, 0x03); + spca506_WriteI2c(gspca_dev, 0x00, 0x04); + spca506_WriteI2c(gspca_dev, 0x00, 0x05); + spca506_WriteI2c(gspca_dev, 0x0d, 0x06); + spca506_WriteI2c(gspca_dev, 0xf0, 0x07); + spca506_WriteI2c(gspca_dev, 0x98, 0x08); + spca506_WriteI2c(gspca_dev, 0x03, 0x09); + spca506_WriteI2c(gspca_dev, 0x80, 0x0a); + spca506_WriteI2c(gspca_dev, 0x47, 0x0b); + spca506_WriteI2c(gspca_dev, 0x48, 0x0c); + spca506_WriteI2c(gspca_dev, 0x00, 0x0d); + spca506_WriteI2c(gspca_dev, 0x03, 0x0e); /* Chroma Pal adjust */ + spca506_WriteI2c(gspca_dev, 0x2a, 0x0f); + spca506_WriteI2c(gspca_dev, 0x00, 0x10); + spca506_WriteI2c(gspca_dev, 0x0c, 0x11); + spca506_WriteI2c(gspca_dev, 0xb8, 0x12); + spca506_WriteI2c(gspca_dev, 0x01, 0x13); + spca506_WriteI2c(gspca_dev, 0x00, 0x14); + spca506_WriteI2c(gspca_dev, 0x00, 0x15); + spca506_WriteI2c(gspca_dev, 0x00, 0x16); + spca506_WriteI2c(gspca_dev, 0x00, 0x17); + spca506_WriteI2c(gspca_dev, 0x00, 0x18); + spca506_WriteI2c(gspca_dev, 0x00, 0x19); + spca506_WriteI2c(gspca_dev, 0x00, 0x1a); + spca506_WriteI2c(gspca_dev, 0x00, 0x1b); + spca506_WriteI2c(gspca_dev, 0x00, 0x1c); + spca506_WriteI2c(gspca_dev, 0x00, 0x1d); + spca506_WriteI2c(gspca_dev, 0x00, 0x1e); + spca506_WriteI2c(gspca_dev, 0xa1, 0x1f); + spca506_WriteI2c(gspca_dev, 0x02, 0x40); + spca506_WriteI2c(gspca_dev, 0xff, 0x41); + spca506_WriteI2c(gspca_dev, 0xff, 0x42); + spca506_WriteI2c(gspca_dev, 0xff, 0x43); + spca506_WriteI2c(gspca_dev, 0xff, 0x44); + spca506_WriteI2c(gspca_dev, 0xff, 0x45); + spca506_WriteI2c(gspca_dev, 0xff, 0x46); + spca506_WriteI2c(gspca_dev, 0xff, 0x47); + spca506_WriteI2c(gspca_dev, 0xff, 0x48); + spca506_WriteI2c(gspca_dev, 0xff, 0x49); + spca506_WriteI2c(gspca_dev, 0xff, 0x4a); + spca506_WriteI2c(gspca_dev, 0xff, 0x4b); + spca506_WriteI2c(gspca_dev, 0xff, 0x4c); + spca506_WriteI2c(gspca_dev, 0xff, 0x4d); + spca506_WriteI2c(gspca_dev, 0xff, 0x4e); + spca506_WriteI2c(gspca_dev, 0xff, 0x4f); + spca506_WriteI2c(gspca_dev, 0xff, 0x50); + spca506_WriteI2c(gspca_dev, 0xff, 0x51); + spca506_WriteI2c(gspca_dev, 0xff, 0x52); + spca506_WriteI2c(gspca_dev, 0xff, 0x53); + spca506_WriteI2c(gspca_dev, 0xff, 0x54); + spca506_WriteI2c(gspca_dev, 0xff, 0x55); + spca506_WriteI2c(gspca_dev, 0xff, 0x56); + spca506_WriteI2c(gspca_dev, 0xff, 0x57); + spca506_WriteI2c(gspca_dev, 0x00, 0x58); + spca506_WriteI2c(gspca_dev, 0x54, 0x59); + spca506_WriteI2c(gspca_dev, 0x07, 0x5a); + spca506_WriteI2c(gspca_dev, 0x83, 0x5b); + spca506_WriteI2c(gspca_dev, 0x00, 0x5c); + spca506_WriteI2c(gspca_dev, 0x00, 0x5d); + spca506_WriteI2c(gspca_dev, 0x00, 0x5e); + spca506_WriteI2c(gspca_dev, 0x00, 0x5f); + spca506_WriteI2c(gspca_dev, 0x00, 0x60); + spca506_WriteI2c(gspca_dev, 0x05, 0x61); + spca506_WriteI2c(gspca_dev, 0x9f, 0x62); + PDEBUG(D_STREAM, "** Close Init *"); + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u16 norme; + __u16 channel; + __u8 Data[2]; + + /**************************************/ + reg_w(dev, 0x03, 0x00, 0x0004); + reg_w(dev, 0x03, 0x00, 0x0003); + reg_w(dev, 0x03, 0x00, 0x0004); + reg_w(dev, 0x03, 0xFF, 0x0003); + reg_w(dev, 0x02, 0x00, 0x0000); + reg_w(dev, 0x03, 0x60, 0x0000); + reg_w(dev, 0x03, 0x18, 0x0001); + + /*sdca506_WriteI2c(value,register) */ + spca506_Initi2c(gspca_dev); + spca506_WriteI2c(gspca_dev, 0x08, 0x01); /* Increment Delay */ +/* spca506_WriteI2c(gspca_dev, 0xc0, 0x02); * Analog Input Control 1 */ + spca506_WriteI2c(gspca_dev, 0x33, 0x03); + /* Analog Input Control 2 */ + spca506_WriteI2c(gspca_dev, 0x00, 0x04); + /* Analog Input Control 3 */ + spca506_WriteI2c(gspca_dev, 0x00, 0x05); + /* Analog Input Control 4 */ + spca506_WriteI2c(gspca_dev, 0x0d, 0x06); + /* Horizontal Sync Start 0xe9-0x0d */ + spca506_WriteI2c(gspca_dev, 0xf0, 0x07); + /* Horizontal Sync Stop 0x0d-0xf0 */ + + spca506_WriteI2c(gspca_dev, 0x98, 0x08); /* Sync Control */ +/* Defaults value */ + spca506_WriteI2c(gspca_dev, 0x03, 0x09); /* Luminance Control */ + spca506_WriteI2c(gspca_dev, 0x80, 0x0a); + /* Luminance Brightness */ + spca506_WriteI2c(gspca_dev, 0x47, 0x0b); /* Luminance Contrast */ + spca506_WriteI2c(gspca_dev, 0x48, 0x0c); + /* Chrominance Saturation */ + spca506_WriteI2c(gspca_dev, 0x00, 0x0d); + /* Chrominance Hue Control */ + spca506_WriteI2c(gspca_dev, 0x2a, 0x0f); + /* Chrominance Gain Control */ + /**************************************/ + spca506_WriteI2c(gspca_dev, 0x00, 0x10); + /* Format/Delay Control */ + spca506_WriteI2c(gspca_dev, 0x0c, 0x11); /* Output Control 1 */ + spca506_WriteI2c(gspca_dev, 0xb8, 0x12); /* Output Control 2 */ + spca506_WriteI2c(gspca_dev, 0x01, 0x13); /* Output Control 3 */ + spca506_WriteI2c(gspca_dev, 0x00, 0x14); /* reserved */ + spca506_WriteI2c(gspca_dev, 0x00, 0x15); /* VGATE START */ + spca506_WriteI2c(gspca_dev, 0x00, 0x16); /* VGATE STOP */ + spca506_WriteI2c(gspca_dev, 0x00, 0x17); /* VGATE Control (MSB) */ + spca506_WriteI2c(gspca_dev, 0x00, 0x18); + spca506_WriteI2c(gspca_dev, 0x00, 0x19); + spca506_WriteI2c(gspca_dev, 0x00, 0x1a); + spca506_WriteI2c(gspca_dev, 0x00, 0x1b); + spca506_WriteI2c(gspca_dev, 0x00, 0x1c); + spca506_WriteI2c(gspca_dev, 0x00, 0x1d); + spca506_WriteI2c(gspca_dev, 0x00, 0x1e); + spca506_WriteI2c(gspca_dev, 0xa1, 0x1f); + spca506_WriteI2c(gspca_dev, 0x02, 0x40); + spca506_WriteI2c(gspca_dev, 0xff, 0x41); + spca506_WriteI2c(gspca_dev, 0xff, 0x42); + spca506_WriteI2c(gspca_dev, 0xff, 0x43); + spca506_WriteI2c(gspca_dev, 0xff, 0x44); + spca506_WriteI2c(gspca_dev, 0xff, 0x45); + spca506_WriteI2c(gspca_dev, 0xff, 0x46); + spca506_WriteI2c(gspca_dev, 0xff, 0x47); + spca506_WriteI2c(gspca_dev, 0xff, 0x48); + spca506_WriteI2c(gspca_dev, 0xff, 0x49); + spca506_WriteI2c(gspca_dev, 0xff, 0x4a); + spca506_WriteI2c(gspca_dev, 0xff, 0x4b); + spca506_WriteI2c(gspca_dev, 0xff, 0x4c); + spca506_WriteI2c(gspca_dev, 0xff, 0x4d); + spca506_WriteI2c(gspca_dev, 0xff, 0x4e); + spca506_WriteI2c(gspca_dev, 0xff, 0x4f); + spca506_WriteI2c(gspca_dev, 0xff, 0x50); + spca506_WriteI2c(gspca_dev, 0xff, 0x51); + spca506_WriteI2c(gspca_dev, 0xff, 0x52); + spca506_WriteI2c(gspca_dev, 0xff, 0x53); + spca506_WriteI2c(gspca_dev, 0xff, 0x54); + spca506_WriteI2c(gspca_dev, 0xff, 0x55); + spca506_WriteI2c(gspca_dev, 0xff, 0x56); + spca506_WriteI2c(gspca_dev, 0xff, 0x57); + spca506_WriteI2c(gspca_dev, 0x00, 0x58); + spca506_WriteI2c(gspca_dev, 0x54, 0x59); + spca506_WriteI2c(gspca_dev, 0x07, 0x5a); + spca506_WriteI2c(gspca_dev, 0x83, 0x5b); + spca506_WriteI2c(gspca_dev, 0x00, 0x5c); + spca506_WriteI2c(gspca_dev, 0x00, 0x5d); + spca506_WriteI2c(gspca_dev, 0x00, 0x5e); + spca506_WriteI2c(gspca_dev, 0x00, 0x5f); + spca506_WriteI2c(gspca_dev, 0x00, 0x60); + spca506_WriteI2c(gspca_dev, 0x05, 0x61); + spca506_WriteI2c(gspca_dev, 0x9f, 0x62); + /**************************************/ + reg_w(dev, 0x05, 0x00, 0x0003); + reg_w(dev, 0x05, 0x00, 0x0004); + reg_w(dev, 0x03, 0x10, 0x0001); + reg_w(dev, 0x03, 0x78, 0x0000); + switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode) { + case 0: + spca506_Setsize(gspca_dev, 0, 0x10, 0x10); + break; + case 1: + spca506_Setsize(gspca_dev, 1, 0x1a, 0x1a); + break; + case 2: + spca506_Setsize(gspca_dev, 2, 0x1c, 0x1c); + break; + case 4: + spca506_Setsize(gspca_dev, 4, 0x34, 0x34); + break; + default: +/* case 5: */ + spca506_Setsize(gspca_dev, 5, 0x40, 0x40); + break; + } + + /* compress setting and size */ + /* set i2c luma */ + reg_w(dev, 0x02, 0x01, 0x0000); + reg_w(dev, 0x03, 0x12, 0x000); + reg_r(dev, 0x04, 0x0001, Data, 2); + PDEBUG(D_STREAM, "webcam started"); + spca506_GetNormeInput(gspca_dev, &norme, &channel); + spca506_SetNormeInput(gspca_dev, norme, channel); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + + reg_w(dev, 0x02, 0x00, 0x0000); + reg_w(dev, 0x03, 0x00, 0x0004); + reg_w(dev, 0x03, 0x00, 0x0003); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +/* convert YYUV per line to YUYV (YUV 4:2:2) */ +static void yyuv_decode(unsigned char *out, + unsigned char *in, + int width, + int height) +{ + unsigned char *Ui, *Vi, *yi, *yi1; + unsigned char *out1; + int i, j; + + yi = in; + for (i = height / 2; --i >= 0; ) { + out1 = out + width * 2; /* next line */ + yi1 = yi + width; + Ui = yi1 + width; + Vi = Ui + width / 2; + for (j = width / 2; --j >= 0; ) { + *out++ = 128 + *yi++; + *out++ = 128 + *Ui; + *out++ = 128 + *yi++; + *out++ = 128 + *Vi; + + *out1++ = 128 + *yi1++; + *out1++ = 128 + *Ui++; + *out1++ = 128 + *yi1++; + *out1++ = 128 + *Vi++; + } + yi += width * 2; + out = out1; + } +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + unsigned char *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (data[0]) { + case 0: /* start of frame */ + if (gspca_dev->last_packet_type == FIRST_PACKET) { + yyuv_decode(sd->tmpbuf2, sd->tmpbuf, + gspca_dev->width, + gspca_dev->height); + frame = gspca_frame_add(gspca_dev, + LAST_PACKET, + frame, + sd->tmpbuf2, + gspca_dev->width + * gspca_dev->height + * 2); + } + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, 0); + data += SPCA50X_OFFSET_DATA; + len -= SPCA50X_OFFSET_DATA; + if (len > 0) + memcpy(sd->tmpbuf, data, len); + else + len = 0; + sd->buflen = len; + return; + case 0xff: /* drop */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + data += 1; + len -= 1; + memcpy(&sd->tmpbuf[sd->buflen], data, len); + sd->buflen += len; +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + spca506_Initi2c(gspca_dev); + spca506_WriteI2c(gspca_dev, sd->brightness, SAA7113_bright); + spca506_WriteI2c(gspca_dev, 0x01, 0x09); +} + +static void getbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = spca506_ReadI2c(gspca_dev, SAA7113_bright); +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + spca506_Initi2c(gspca_dev); + spca506_WriteI2c(gspca_dev, sd->contrast, SAA7113_contrast); + spca506_WriteI2c(gspca_dev, 0x01, 0x09); +} + +static void getcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = spca506_ReadI2c(gspca_dev, SAA7113_contrast); +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + spca506_Initi2c(gspca_dev); + spca506_WriteI2c(gspca_dev, sd->colors, SAA7113_saturation); + spca506_WriteI2c(gspca_dev, 0x01, 0x09); +} + +static void getcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = spca506_ReadI2c(gspca_dev, SAA7113_saturation); +} + +static void sethue(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + spca506_Initi2c(gspca_dev); + spca506_WriteI2c(gspca_dev, sd->hue, SAA7113_hue); + spca506_WriteI2c(gspca_dev, 0x01, 0x09); +} + +static void gethue(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hue = spca506_ReadI2c(gspca_dev, SAA7113_hue); +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcontrast(gspca_dev); + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcolors(gspca_dev); + *val = sd->colors; + return 0; +} + +static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hue = val; + if (gspca_dev->streaming) + sethue(gspca_dev); + return 0; +} + +static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + gethue(gspca_dev); + *val = sd->hue; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x06e1, 0xa190), DVNM("ADS Instant VCD")}, +/* {USB_DEVICE(0x0733, 0x0430), DVNM("UsbGrabber PV321c")}, */ + {USB_DEVICE(0x0734, 0x043b), DVNM("3DeMon USB Capture aka")}, + {USB_DEVICE(0x99fa, 0x8988), DVNM("Grandtec V.cap")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c new file mode 100644 index 000000000000..566adf41f59f --- /dev/null +++ b/drivers/media/video/gspca/spca508.c @@ -0,0 +1,1774 @@ +/* + * SPCA508 chip based cameras subdriver + * + * V4L2 by Jean-Francois Moine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "spca508" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/SPCA508 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + int buflen; + unsigned char tmpbuf[352 * 288 * 3 / 2]; /* YUVY per line */ + unsigned char tmpbuf2[352 * 288 * 2]; /* YUYV */ + + unsigned char brightness; + + char subtype; +#define CreativeVista 0 +#define HamaUSBSightcam 1 +#define HamaUSBSightcam2 2 +#define IntelEasyPCCamera 3 +#define MicroInnovationIC200 4 +#define ViewQuestVQ110 5 +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x80, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +}; + +static struct cam_mode sif_mode[] = { + {V4L2_PIX_FMT_YUYV, 160, 120, 3}, + {V4L2_PIX_FMT_YUYV, 176, 144, 2}, + {V4L2_PIX_FMT_YUYV, 320, 240, 1}, + {V4L2_PIX_FMT_YUYV, 352, 288, 0}, +}; + +/* Frame packet header offsets for the spca508 */ +#define SPCA508_OFFSET_TYPE 1 +#define SPCA508_OFFSET_COMPRESS 2 +#define SPCA508_OFFSET_FRAMSEQ 8 +#define SPCA508_OFFSET_WIN1LUM 11 +#define SPCA508_OFFSET_DATA 37 + +#define SPCA508_SNAPBIT 0x20 +#define SPCA508_SNAPCTRL 0x40 +/*************** I2c ****************/ +#define SPCA508_INDEX_I2C_BASE 0x8800 + +/* + * Initialization data: this is the first set-up data written to the + * device (before the open data). + */ +static __u16 spca508_init_data[][3] = +#define IGN(x) /* nothing */ +{ + /* line URB value, index */ + /* 44274 1804 */ {0x0000, 0x870b}, + + /* 44299 1805 */ {0x0020, 0x8112}, + /* Video drop enable, ISO streaming disable */ + /* 44324 1806 */ {0x0003, 0x8111}, + /* Reset compression & memory */ + /* 44349 1807 */ {0x0000, 0x8110}, + /* Disable all outputs */ + /* 44372 1808 */ /* READ {0x0000, 0x8114} -> 0000: 00 */ + /* 44398 1809 */ {0x0000, 0x8114}, + /* SW GPIO data */ + /* 44423 1810 */ {0x0008, 0x8110}, + /* Enable charge pump output */ + /* 44527 1811 */ {0x0002, 0x8116}, + /* 200 kHz pump clock */ + /* 44555 1812 */ + /* UNKNOWN DIRECTION (URB_FUNCTION_SELECT_INTERFACE:) */ + /* 44590 1813 */ {0x0003, 0x8111}, + /* Reset compression & memory */ + /* 44615 1814 */ {0x0000, 0x8111}, + /* Normal mode (not reset) */ + /* 44640 1815 */ {0x0098, 0x8110}, + /* Enable charge pump output, sync.serial,external 2x clock */ + /* 44665 1816 */ {0x000d, 0x8114}, + /* SW GPIO data */ + /* 44690 1817 */ {0x0002, 0x8116}, + /* 200 kHz pump clock */ + /* 44715 1818 */ {0x0020, 0x8112}, + /* Video drop enable, ISO streaming disable */ +/* --------------------------------------- */ + /* 44740 1819 */ {0x000f, 0x8402}, + /* memory bank */ + /* 44765 1820 */ {0x0000, 0x8403}, + /* ... address */ +/* --------------------------------------- */ +/* 0x88__ is Synchronous Serial Interface. */ +/* TBD: This table could be expressed more compactly */ +/* using spca508_write_i2c_vector(). */ +/* TBD: Should see if the values in spca50x_i2c_data */ +/* would work with the VQ110 instead of the values */ +/* below. */ + /* 44790 1821 */ {0x00c0, 0x8804}, + /* SSI slave addr */ + /* 44815 1822 */ {0x0008, 0x8802}, + /* 375 Khz SSI clock */ + /* 44838 1823 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 44862 1824 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 44888 1825 */ {0x0008, 0x8802}, + /* 375 Khz SSI clock */ + /* 44913 1826 */ {0x0012, 0x8801}, + /* SSI reg addr */ + /* 44938 1827 */ {0x0080, 0x8800}, + /* SSI data to write */ + /* 44961 1828 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 44985 1829 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45009 1830 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 45035 1831 */ {0x0008, 0x8802}, + /* 375 Khz SSI clock */ + /* 45060 1832 */ {0x0012, 0x8801}, + /* SSI reg addr */ + /* 45085 1833 */ {0x0000, 0x8800}, + /* SSI data to write */ + /* 45108 1834 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45132 1835 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45156 1836 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 45182 1837 */ {0x0008, 0x8802}, + /* 375 Khz SSI clock */ + /* 45207 1838 */ {0x0011, 0x8801}, + /* SSI reg addr */ + /* 45232 1839 */ {0x0040, 0x8800}, + /* SSI data to write */ + /* 45255 1840 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45279 1841 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45303 1842 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 45329 1843 */ {0x0008, 0x8802}, + /* 45354 1844 */ {0x0013, 0x8801}, + /* 45379 1845 */ {0x0000, 0x8800}, + /* 45402 1846 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45426 1847 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45450 1848 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 45476 1849 */ {0x0008, 0x8802}, + /* 45501 1850 */ {0x0014, 0x8801}, + /* 45526 1851 */ {0x0000, 0x8800}, + /* 45549 1852 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45573 1853 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45597 1854 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 45623 1855 */ {0x0008, 0x8802}, + /* 45648 1856 */ {0x0015, 0x8801}, + /* 45673 1857 */ {0x0001, 0x8800}, + /* 45696 1858 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45720 1859 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45744 1860 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 45770 1861 */ {0x0008, 0x8802}, + /* 45795 1862 */ {0x0016, 0x8801}, + /* 45820 1863 */ {0x0003, 0x8800}, + /* 45843 1864 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45867 1865 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 45891 1866 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 45917 1867 */ {0x0008, 0x8802}, + /* 45942 1868 */ {0x0017, 0x8801}, + /* 45967 1869 */ {0x0036, 0x8800}, + /* 45990 1870 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46014 1871 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46038 1872 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 46064 1873 */ {0x0008, 0x8802}, + /* 46089 1874 */ {0x0018, 0x8801}, + /* 46114 1875 */ {0x00ec, 0x8800}, + /* 46137 1876 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46161 1877 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46185 1878 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 46211 1879 */ {0x0008, 0x8802}, + /* 46236 1880 */ {0x001a, 0x8801}, + /* 46261 1881 */ {0x0094, 0x8800}, + /* 46284 1882 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46308 1883 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46332 1884 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 46358 1885 */ {0x0008, 0x8802}, + /* 46383 1886 */ {0x001b, 0x8801}, + /* 46408 1887 */ {0x0000, 0x8800}, + /* 46431 1888 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46455 1889 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46479 1890 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 46505 1891 */ {0x0008, 0x8802}, + /* 46530 1892 */ {0x0027, 0x8801}, + /* 46555 1893 */ {0x00a2, 0x8800}, + /* 46578 1894 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46602 1895 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46626 1896 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 46652 1897 */ {0x0008, 0x8802}, + /* 46677 1898 */ {0x0028, 0x8801}, + /* 46702 1899 */ {0x0040, 0x8800}, + /* 46725 1900 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46749 1901 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46773 1902 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 46799 1903 */ {0x0008, 0x8802}, + /* 46824 1904 */ {0x002a, 0x8801}, + /* 46849 1905 */ {0x0084, 0x8800}, + /* 46872 1906 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46896 1907 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 46920 1908 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 46946 1909 */ {0x0008, 0x8802}, + /* 46971 1910 */ {0x002b, 0x8801}, + /* 46996 1911 */ {0x00a8, 0x8800}, + /* 47019 1912 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47043 1913 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47067 1914 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 47093 1915 */ {0x0008, 0x8802}, + /* 47118 1916 */ {0x002c, 0x8801}, + /* 47143 1917 */ {0x00fe, 0x8800}, + /* 47166 1918 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47190 1919 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47214 1920 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 47240 1921 */ {0x0008, 0x8802}, + /* 47265 1922 */ {0x002d, 0x8801}, + /* 47290 1923 */ {0x0003, 0x8800}, + /* 47313 1924 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47337 1925 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47361 1926 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 47387 1927 */ {0x0008, 0x8802}, + /* 47412 1928 */ {0x0038, 0x8801}, + /* 47437 1929 */ {0x0083, 0x8800}, + /* 47460 1930 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47484 1931 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47508 1932 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 47534 1933 */ {0x0008, 0x8802}, + /* 47559 1934 */ {0x0033, 0x8801}, + /* 47584 1935 */ {0x0081, 0x8800}, + /* 47607 1936 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47631 1937 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47655 1938 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 47681 1939 */ {0x0008, 0x8802}, + /* 47706 1940 */ {0x0034, 0x8801}, + /* 47731 1941 */ {0x004a, 0x8800}, + /* 47754 1942 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47778 1943 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47802 1944 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 47828 1945 */ {0x0008, 0x8802}, + /* 47853 1946 */ {0x0039, 0x8801}, + /* 47878 1947 */ {0x0000, 0x8800}, + /* 47901 1948 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47925 1949 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 47949 1950 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 47975 1951 */ {0x0008, 0x8802}, + /* 48000 1952 */ {0x0010, 0x8801}, + /* 48025 1953 */ {0x00a8, 0x8800}, + /* 48048 1954 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48072 1955 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48096 1956 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 48122 1957 */ {0x0008, 0x8802}, + /* 48147 1958 */ {0x0006, 0x8801}, + /* 48172 1959 */ {0x0058, 0x8800}, + /* 48195 1960 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48219 1961 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48243 1962 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 48269 1963 */ {0x0008, 0x8802}, + /* 48294 1964 */ {0x0000, 0x8801}, + /* 48319 1965 */ {0x0004, 0x8800}, + /* 48342 1966 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48366 1967 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48390 1968 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 48416 1969 */ {0x0008, 0x8802}, + /* 48441 1970 */ {0x0040, 0x8801}, + /* 48466 1971 */ {0x0080, 0x8800}, + /* 48489 1972 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48513 1973 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48537 1974 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 48563 1975 */ {0x0008, 0x8802}, + /* 48588 1976 */ {0x0041, 0x8801}, + /* 48613 1977 */ {0x000c, 0x8800}, + /* 48636 1978 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48660 1979 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48684 1980 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 48710 1981 */ {0x0008, 0x8802}, + /* 48735 1982 */ {0x0042, 0x8801}, + /* 48760 1983 */ {0x000c, 0x8800}, + /* 48783 1984 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48807 1985 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48831 1986 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 48857 1987 */ {0x0008, 0x8802}, + /* 48882 1988 */ {0x0043, 0x8801}, + /* 48907 1989 */ {0x0028, 0x8800}, + /* 48930 1990 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48954 1991 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 48978 1992 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 49004 1993 */ {0x0008, 0x8802}, + /* 49029 1994 */ {0x0044, 0x8801}, + /* 49054 1995 */ {0x0080, 0x8800}, + /* 49077 1996 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49101 1997 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49125 1998 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 49151 1999 */ {0x0008, 0x8802}, + /* 49176 2000 */ {0x0045, 0x8801}, + /* 49201 2001 */ {0x0020, 0x8800}, + /* 49224 2002 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49248 2003 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49272 2004 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 49298 2005 */ {0x0008, 0x8802}, + /* 49323 2006 */ {0x0046, 0x8801}, + /* 49348 2007 */ {0x0020, 0x8800}, + /* 49371 2008 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49395 2009 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49419 2010 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 49445 2011 */ {0x0008, 0x8802}, + /* 49470 2012 */ {0x0047, 0x8801}, + /* 49495 2013 */ {0x0080, 0x8800}, + /* 49518 2014 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49542 2015 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49566 2016 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 49592 2017 */ {0x0008, 0x8802}, + /* 49617 2018 */ {0x0048, 0x8801}, + /* 49642 2019 */ {0x004c, 0x8800}, + /* 49665 2020 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49689 2021 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49713 2022 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 49739 2023 */ {0x0008, 0x8802}, + /* 49764 2024 */ {0x0049, 0x8801}, + /* 49789 2025 */ {0x0084, 0x8800}, + /* 49812 2026 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49836 2027 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49860 2028 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 49886 2029 */ {0x0008, 0x8802}, + /* 49911 2030 */ {0x004a, 0x8801}, + /* 49936 2031 */ {0x0084, 0x8800}, + /* 49959 2032 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 49983 2033 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 50007 2034 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 50033 2035 */ {0x0008, 0x8802}, + /* 50058 2036 */ {0x004b, 0x8801}, + /* 50083 2037 */ {0x0084, 0x8800}, + /* 50106 2038 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* --------------------------------------- */ + /* 50132 2039 */ {0x0012, 0x8700}, + /* Clock speed 48Mhz/(2+2)/2= 6 Mhz */ + /* 50157 2040 */ {0x0000, 0x8701}, + /* CKx1 clock delay adj */ + /* 50182 2041 */ {0x0000, 0x8701}, + /* CKx1 clock delay adj */ + /* 50207 2042 */ {0x0001, 0x870c}, + /* CKOx2 output */ + /* --------------------------------------- */ + /* 50232 2043 */ {0x0080, 0x8600}, + /* Line memory read counter (L) */ + /* 50257 2044 */ {0x0001, 0x8606}, + /* reserved */ + /* 50282 2045 */ {0x0064, 0x8607}, + /* Line memory read counter (H) 0x6480=25,728 */ + /* 50307 2046 */ {0x002a, 0x8601}, + /* CDSP sharp interpolation mode, + * line sel for color sep, edge enhance enab */ + /* 50332 2047 */ {0x0000, 0x8602}, + /* optical black level for user settng = 0 */ + /* 50357 2048 */ {0x0080, 0x8600}, + /* Line memory read counter (L) */ + /* 50382 2049 */ {0x000a, 0x8603}, + /* optical black level calc mode: auto; optical black offset = 10 */ + /* 50407 2050 */ {0x00df, 0x865b}, + /* Horiz offset for valid pixels (L)=0xdf */ + /* 50432 2051 */ {0x0012, 0x865c}, + /* Vert offset for valid lines (L)=0x12 */ + +/* The following two lines seem to be the "wrong" resolution. */ +/* But perhaps these indicate the actual size of the sensor */ +/* rather than the size of the current video mode. */ + /* 50457 2052 */ {0x0058, 0x865d}, + /* Horiz valid pixels (*4) (L) = 352 */ + /* 50482 2053 */ {0x0048, 0x865e}, + /* Vert valid lines (*4) (L) = 288 */ + + /* 50507 2054 */ {0x0015, 0x8608}, + /* A11 Coef ... */ + /* 50532 2055 */ {0x0030, 0x8609}, + /* 50557 2056 */ {0x00fb, 0x860a}, + /* 50582 2057 */ {0x003e, 0x860b}, + /* 50607 2058 */ {0x00ce, 0x860c}, + /* 50632 2059 */ {0x00f4, 0x860d}, + /* 50657 2060 */ {0x00eb, 0x860e}, + /* 50682 2061 */ {0x00dc, 0x860f}, + /* 50707 2062 */ {0x0039, 0x8610}, + /* 50732 2063 */ {0x0001, 0x8611}, + /* R offset for white balance ... */ + /* 50757 2064 */ {0x0000, 0x8612}, + /* 50782 2065 */ {0x0001, 0x8613}, + /* 50807 2066 */ {0x0000, 0x8614}, + /* 50832 2067 */ {0x005b, 0x8651}, + /* R gain for white balance ... */ + /* 50857 2068 */ {0x0040, 0x8652}, + /* 50882 2069 */ {0x0060, 0x8653}, + /* 50907 2070 */ {0x0040, 0x8654}, + /* 50932 2071 */ {0x0000, 0x8655}, + /* 50957 2072 */ {0x0001, 0x863f}, + /* Fixed gamma correction enable, USB control, + * lum filter disable, lum noise clip disable */ + /* 50982 2073 */ {0x00a1, 0x8656}, + /* Window1 size 256x256, Windows2 size 64x64, + * gamma look-up disable, new edge enhancement enable */ + /* 51007 2074 */ {0x0018, 0x8657}, + /* Edge gain high thresh */ + /* 51032 2075 */ {0x0020, 0x8658}, + /* Edge gain low thresh */ + /* 51057 2076 */ {0x000a, 0x8659}, + /* Edge bandwidth high threshold */ + /* 51082 2077 */ {0x0005, 0x865a}, + /* Edge bandwidth low threshold */ + /* -------------------------------- */ + /* 51107 2078 */ {0x0030, 0x8112}, + /* Video drop enable, ISO streaming enable */ + /* 51130 2079 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 51154 2080 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 51180 2081 */ {0xa908, 0x8802}, + /* 51205 2082 */ {0x0034, 0x8801}, + /* SSI reg addr */ + /* 51230 2083 */ {0x00ca, 0x8800}, + /* SSI data to write */ + /* 51253 2084 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 51277 2085 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 51301 2086 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 51327 2087 */ {0x1f08, 0x8802}, + /* 51352 2088 */ {0x0006, 0x8801}, + /* 51377 2089 */ {0x0080, 0x8800}, + /* 51400 2090 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + +/* ----- Read back coefs we wrote earlier. */ + /* 51424 2091 */ /* READ { 0, 0x0000, 0x8608 } -> 0000: 15 */ + /* 51448 2092 */ /* READ { 0, 0x0000, 0x8609 } -> 0000: 30 */ + /* 51472 2093 */ /* READ { 0, 0x0000, 0x860a } -> 0000: fb */ + /* 51496 2094 */ /* READ { 0, 0x0000, 0x860b } -> 0000: 3e */ + /* 51520 2095 */ /* READ { 0, 0x0000, 0x860c } -> 0000: ce */ + /* 51544 2096 */ /* READ { 0, 0x0000, 0x860d } -> 0000: f4 */ + /* 51568 2097 */ /* READ { 0, 0x0000, 0x860e } -> 0000: eb */ + /* 51592 2098 */ /* READ { 0, 0x0000, 0x860f } -> 0000: dc */ + /* 51616 2099 */ /* READ { 0, 0x0000, 0x8610 } -> 0000: 39 */ + /* 51640 2100 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 51664 2101 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ + /* 51690 2102 */ {0xb008, 0x8802}, + /* 51715 2103 */ {0x0006, 0x8801}, + /* 51740 2104 */ {0x007d, 0x8800}, + /* 51763 2105 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + + + /* This chunk is seemingly redundant with */ + /* earlier commands (A11 Coef...), but if I disable it, */ + /* the image appears too dark. Maybe there was some kind of */ + /* reset since the earlier commands, so this is necessary again. */ + /* 51789 2106 */ {0x0015, 0x8608}, + /* 51814 2107 */ {0x0030, 0x8609}, + /* 51839 2108 */ {0xfffb, 0x860a}, + /* 51864 2109 */ {0x003e, 0x860b}, + /* 51889 2110 */ {0xffce, 0x860c}, + /* 51914 2111 */ {0xfff4, 0x860d}, + /* 51939 2112 */ {0xffeb, 0x860e}, + /* 51964 2113 */ {0xffdc, 0x860f}, + /* 51989 2114 */ {0x0039, 0x8610}, + /* 52014 2115 */ {0x0018, 0x8657}, + + /* 52039 2116 */ {0x0000, 0x8508}, + /* Disable compression. */ + /* Previous line was: + * 52039 2116 * { 0, 0x0021, 0x8508 }, * Enable compression. */ + /* 52064 2117 */ {0x0032, 0x850b}, + /* compression stuff */ + /* 52089 2118 */ {0x0003, 0x8509}, + /* compression stuff */ + /* 52114 2119 */ {0x0011, 0x850a}, + /* compression stuff */ + /* 52139 2120 */ {0x0021, 0x850d}, + /* compression stuff */ + /* 52164 2121 */ {0x0010, 0x850c}, + /* compression stuff */ + /* 52189 2122 */ {0x0003, 0x8500}, + /* *** Video mode: 160x120 */ + /* 52214 2123 */ {0x0001, 0x8501}, + /* Hardware-dominated snap control */ + /* 52239 2124 */ {0x0061, 0x8656}, + /* Window1 size 128x128, Windows2 size 128x128, + * gamma look-up disable, new edge enhancement enable */ + /* 52264 2125 */ {0x0018, 0x8617}, + /* Window1 start X (*2) */ + /* 52289 2126 */ {0x0008, 0x8618}, + /* Window1 start Y (*2) */ + /* 52314 2127 */ {0x0061, 0x8656}, + /* Window1 size 128x128, Windows2 size 128x128, + * gamma look-up disable, new edge enhancement enable */ + /* 52339 2128 */ {0x0058, 0x8619}, + /* Window2 start X (*2) */ + /* 52364 2129 */ {0x0008, 0x861a}, + /* Window2 start Y (*2) */ + /* 52389 2130 */ {0x00ff, 0x8615}, + /* High lum thresh for white balance */ + /* 52414 2131 */ {0x0000, 0x8616}, + /* Low lum thresh for white balance */ + /* 52439 2132 */ {0x0012, 0x8700}, + /* Clock speed 48Mhz/(2+2)/2= 6 Mhz */ + /* 52464 2133 */ {0x0012, 0x8700}, + /* Clock speed 48Mhz/(2+2)/2= 6 Mhz */ + /* 52487 2134 */ /* READ { 0, 0x0000, 0x8656 } -> 0000: 61 */ + /* 52513 2135 */ {0x0028, 0x8802}, + /* 375 Khz SSI clock, SSI r/w sync with VSYNC */ + /* 52536 2136 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 52560 2137 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 28 */ + /* 52586 2138 */ {0x1f28, 0x8802}, + /* 375 Khz SSI clock, SSI r/w sync with VSYNC */ + /* 52611 2139 */ {0x0010, 0x8801}, + /* SSI reg addr */ + /* 52636 2140 */ {0x003e, 0x8800}, + /* SSI data to write */ + /* 52659 2141 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 52685 2142 */ {0x0028, 0x8802}, + /* 52708 2143 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 52732 2144 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 28 */ + /* 52758 2145 */ {0x1f28, 0x8802}, + /* 52783 2146 */ {0x0000, 0x8801}, + /* 52808 2147 */ {0x001f, 0x8800}, + /* 52831 2148 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 52857 2149 */ {0x0001, 0x8602}, + /* optical black level for user settning = 1 */ + + /* Original: */ + /* 52882 2150 */ {0x0023, 0x8700}, + /* Clock speed 48Mhz/(3+2)/4= 2.4 Mhz */ + /* 52907 2151 */ {0x000f, 0x8602}, + /* optical black level for user settning = 15 */ + + /* 52932 2152 */ {0x0028, 0x8802}, + /* 52955 2153 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 52979 2154 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 28 */ + /* 53005 2155 */ {0x1f28, 0x8802}, + /* 53030 2156 */ {0x0010, 0x8801}, + /* 53055 2157 */ {0x007b, 0x8800}, + /* 53078 2158 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* 53104 2159 */ {0x002f, 0x8651}, + /* R gain for white balance ... */ + /* 53129 2160 */ {0x0080, 0x8653}, + /* 53152 2161 */ /* READ { 0, 0x0000, 0x8655 } -> 0000: 00 */ + /* 53178 2162 */ {0x0000, 0x8655}, + + /* 53203 2163 */ {0x0030, 0x8112}, + /* Video drop enable, ISO streaming enable */ + /* 53228 2164 */ {0x0020, 0x8112}, + /* Video drop enable, ISO streaming disable */ + /* 53252 2165 */ + /* UNKNOWN DIRECTION (URB_FUNCTION_SELECT_INTERFACE: (ALT=0) ) */ + {0, 0} +}; + + +/* + * Initialization data for Intel EasyPC Camera CS110 + */ +static __u16 spca508cs110_init_data[][3] = { + {0x0000, 0x870b}, /* Reset CTL3 */ + {0x0003, 0x8111}, /* Soft Reset compression, memory, TG & CDSP */ + {0x0000, 0x8111}, /* Normal operation on reset */ + {0x0090, 0x8110}, + /* External Clock 2x & Synchronous Serial Interface Output */ + {0x0020, 0x8112}, /* Video Drop packet enable */ + {0x0000, 0x8114}, /* Software GPIO output data */ + {0x0001, 0x8114}, + {0x0001, 0x8114}, + {0x0001, 0x8114}, + {0x0003, 0x8114}, + + /* Initial sequence Synchronous Serial Interface */ + {0x000f, 0x8402}, /* Memory bank Address */ + {0x0000, 0x8403}, /* Memory bank Address */ + {0x00ba, 0x8804}, /* SSI Slave address */ + {0x0010, 0x8802}, /* 93.75kHz SSI Clock Two DataByte */ + {0x0010, 0x8802}, /* 93.75kHz SSI Clock two DataByte */ + + {0x0001, 0x8801}, + {0x000a, 0x8805},/* a - NWG: Dunno what this is about */ + {0x0000, 0x8800}, + {0x0010, 0x8802}, + + {0x0002, 0x8801}, + {0x0000, 0x8805}, + {0x0000, 0x8800}, + {0x0010, 0x8802}, + + {0x0003, 0x8801}, + {0x0027, 0x8805}, + {0x0001, 0x8800}, + {0x0010, 0x8802}, + + {0x0004, 0x8801}, + {0x0065, 0x8805}, + {0x0001, 0x8800}, + {0x0010, 0x8802}, + + {0x0005, 0x8801}, + {0x0003, 0x8805}, + {0x0000, 0x8800}, + {0x0010, 0x8802}, + + {0x0006, 0x8801}, + {0x001c, 0x8805}, + {0x0000, 0x8800}, + {0x0010, 0x8802}, + + {0x0007, 0x8801}, + {0x002a, 0x8805}, + {0x0000, 0x8800}, + {0x0010, 0x8802}, + + {0x0002, 0x8704}, /* External input CKIx1 */ + {0x0001, 0x8606}, /* 1 Line memory Read Counter (H) Result: (d)410 */ + {0x009a, 0x8600}, /* Line memory Read Counter (L) */ + {0x0001, 0x865b}, /* 1 Horizontal Offset for Valid Pixel(L) */ + {0x0003, 0x865c}, /* 3 Vertical Offset for Valid Lines(L) */ + {0x0058, 0x865d}, /* 58 Horizontal Valid Pixel Window(L) */ + + {0x0006, 0x8660}, /* Nibble data + input order */ + + {0x000a, 0x8602}, /* Optical black level set to 0x0a */ +/* 1945 */ {0x0000, 0x8603}, /* Optical black level Offset */ + +/* 1962 * {0, 0x0000, 0x8611}, * 0 R Offset for white Balance */ +/* 1963 * {0, 0x0000, 0x8612}, * 1 Gr Offset for white Balance */ +/* 1964 * {0, 0x0000, 0x8613}, * 1f B Offset for white Balance */ +/* 1965 * {0, 0x0000, 0x8614}, * f0 Gb Offset for white Balance */ + + {0x0040, 0x8651}, /* 2b BLUE gain for white balance good at all 60 */ + {0x0030, 0x8652}, /* 41 Gr Gain for white Balance (L) */ + {0x0035, 0x8653}, /* 26 RED gain for white balance */ + {0x0035, 0x8654}, /* 40Gb Gain for white Balance (L) */ + {0x0041, 0x863f}, + /* Fixed Gamma correction enabled (makes colours look better) */ + +/* 2422 */ {0x0000, 0x8655}, + /* High bits for white balance*****brightness control*** */ + {} +}; + +static __u16 spca508_sightcam_init_data[][3] = { +/* This line seems to setup the frame/canvas */ + /*368 */ {0x000f, 0x8402}, + +/* Theese 6 lines are needed to startup the webcam */ + /*398 */ {0x0090, 0x8110}, + /*399 */ {0x0001, 0x8114}, + /*400 */ {0x0001, 0x8114}, + /*401 */ {0x0001, 0x8114}, + /*402 */ {0x0003, 0x8114}, + /*403 */ {0x0080, 0x8804}, + +/* This part seems to make the pictures darker? (autobrightness?) */ + /*436 */ {0x0001, 0x8801}, + /*437 */ {0x0004, 0x8800}, + /*439 */ {0x0003, 0x8801}, + /*440 */ {0x00e0, 0x8800}, + /*442 */ {0x0004, 0x8801}, + /*443 */ {0x00b4, 0x8800}, + /*445 */ {0x0005, 0x8801}, + /*446 */ {0x0000, 0x8800}, + + /*448 */ {0x0006, 0x8801}, + /*449 */ {0x00e0, 0x8800}, + /*451 */ {0x0007, 0x8801}, + /*452 */ {0x000c, 0x8800}, + +/* This section is just needed, it probably + * does something like the previous section, + * but the cam won't start if it's not included. + */ + /*484 */ {0x0014, 0x8801}, + /*485 */ {0x0008, 0x8800}, + /*487 */ {0x0015, 0x8801}, + /*488 */ {0x0067, 0x8800}, + /*490 */ {0x0016, 0x8801}, + /*491 */ {0x0000, 0x8800}, + /*493 */ {0x0017, 0x8801}, + /*494 */ {0x0020, 0x8800}, + /*496 */ {0x0018, 0x8801}, + /*497 */ {0x0044, 0x8800}, + +/* Makes the picture darker - and the + * cam won't start if not included + */ + /*505 */ {0x001e, 0x8801}, + /*506 */ {0x00ea, 0x8800}, + /*508 */ {0x001f, 0x8801}, + /*509 */ {0x0001, 0x8800}, + /*511 */ {0x0003, 0x8801}, + /*512 */ {0x00e0, 0x8800}, + +/* seems to place the colors ontop of each other #1 */ + /*517 */ {0x0006, 0x8704}, + /*518 */ {0x0001, 0x870c}, + /*519 */ {0x0016, 0x8600}, + /*520 */ {0x0002, 0x8606}, + +/* if not included the pictures becomes _very_ dark */ + /*521 */ {0x0064, 0x8607}, + /*522 */ {0x003a, 0x8601}, + /*523 */ {0x0000, 0x8602}, + +/* seems to place the colors ontop of each other #2 */ + /*524 */ {0x0016, 0x8600}, + /*525 */ {0x0018, 0x8617}, + /*526 */ {0x0008, 0x8618}, + /*527 */ {0x00a1, 0x8656}, + +/* webcam won't start if not included */ + /*528 */ {0x0007, 0x865b}, + /*529 */ {0x0001, 0x865c}, + /*530 */ {0x0058, 0x865d}, + /*531 */ {0x0048, 0x865e}, + +/* adjusts the colors */ + /*541 */ {0x0049, 0x8651}, + /*542 */ {0x0040, 0x8652}, + /*543 */ {0x004c, 0x8653}, + /*544 */ {0x0040, 0x8654}, + + {0, 0} +}; + +static __u16 spca508_sightcam2_init_data[][3] = { +/* 35 */ {0x0020, 0x8112}, + +/* 36 */ {0x000f, 0x8402}, +/* 37 */ {0x0000, 0x8403}, + +/* 38 */ {0x0008, 0x8201}, +/* 39 */ {0x0008, 0x8200}, +/* 40 */ {0x0001, 0x8200}, +/* 43 */ {0x0009, 0x8201}, +/* 44 */ {0x0008, 0x8200}, +/* 45 */ {0x0001, 0x8200}, +/* 48 */ {0x000a, 0x8201}, +/* 49 */ {0x0008, 0x8200}, +/* 50 */ {0x0001, 0x8200}, +/* 53 */ {0x000b, 0x8201}, +/* 54 */ {0x0008, 0x8200}, +/* 55 */ {0x0001, 0x8200}, +/* 58 */ {0x000c, 0x8201}, +/* 59 */ {0x0008, 0x8200}, +/* 60 */ {0x0001, 0x8200}, +/* 63 */ {0x000d, 0x8201}, +/* 64 */ {0x0008, 0x8200}, +/* 65 */ {0x0001, 0x8200}, +/* 68 */ {0x000e, 0x8201}, +/* 69 */ {0x0008, 0x8200}, +/* 70 */ {0x0001, 0x8200}, +/* 73 */ {0x0007, 0x8201}, +/* 74 */ {0x0008, 0x8200}, +/* 75 */ {0x0001, 0x8200}, +/* 78 */ {0x000f, 0x8201}, +/* 79 */ {0x0008, 0x8200}, +/* 80 */ {0x0001, 0x8200}, + +/* 84 */ {0x0018, 0x8660}, +/* 85 */ {0x0010, 0x8201}, + +/* 86 */ {0x0008, 0x8200}, +/* 87 */ {0x0001, 0x8200}, +/* 90 */ {0x0011, 0x8201}, +/* 91 */ {0x0008, 0x8200}, +/* 92 */ {0x0001, 0x8200}, + +/* 95 */ {0x0000, 0x86b0}, +/* 96 */ {0x0034, 0x86b1}, +/* 97 */ {0x0000, 0x86b2}, +/* 98 */ {0x0049, 0x86b3}, +/* 99 */ {0x0000, 0x86b4}, +/* 100 */ {0x0000, 0x86b4}, + +/* 101 */ {0x0012, 0x8201}, +/* 102 */ {0x0008, 0x8200}, +/* 103 */ {0x0001, 0x8200}, +/* 106 */ {0x0013, 0x8201}, +/* 107 */ {0x0008, 0x8200}, +/* 108 */ {0x0001, 0x8200}, + +/* 111 */ {0x0001, 0x86b0}, +/* 112 */ {0x00aa, 0x86b1}, +/* 113 */ {0x0000, 0x86b2}, +/* 114 */ {0x00e4, 0x86b3}, +/* 115 */ {0x0000, 0x86b4}, +/* 116 */ {0x0000, 0x86b4}, + +/* 118 */ {0x0018, 0x8660}, + +/* 119 */ {0x0090, 0x8110}, +/* 120 */ {0x0001, 0x8114}, +/* 121 */ {0x0001, 0x8114}, +/* 122 */ {0x0001, 0x8114}, +/* 123 */ {0x0003, 0x8114}, + +/* 124 */ {0x0080, 0x8804}, +/* 157 */ {0x0003, 0x8801}, +/* 158 */ {0x0012, 0x8800}, +/* 160 */ {0x0004, 0x8801}, +/* 161 */ {0x0005, 0x8800}, +/* 163 */ {0x0005, 0x8801}, +/* 164 */ {0x0000, 0x8800}, +/* 166 */ {0x0006, 0x8801}, +/* 167 */ {0x0000, 0x8800}, +/* 169 */ {0x0007, 0x8801}, +/* 170 */ {0x0000, 0x8800}, +/* 172 */ {0x0008, 0x8801}, +/* 173 */ {0x0005, 0x8800}, +/* 175 */ {0x000a, 0x8700}, +/* 176 */ {0x000e, 0x8801}, +/* 177 */ {0x0004, 0x8800}, +/* 179 */ {0x0005, 0x8801}, +/* 180 */ {0x0047, 0x8800}, +/* 182 */ {0x0006, 0x8801}, +/* 183 */ {0x0000, 0x8800}, +/* 185 */ {0x0007, 0x8801}, +/* 186 */ {0x00c0, 0x8800}, +/* 188 */ {0x0008, 0x8801}, +/* 189 */ {0x0003, 0x8800}, +/* 191 */ {0x0013, 0x8801}, +/* 192 */ {0x0001, 0x8800}, +/* 194 */ {0x0009, 0x8801}, +/* 195 */ {0x0000, 0x8800}, +/* 197 */ {0x000a, 0x8801}, +/* 198 */ {0x0000, 0x8800}, +/* 200 */ {0x000b, 0x8801}, +/* 201 */ {0x0000, 0x8800}, +/* 203 */ {0x000c, 0x8801}, +/* 204 */ {0x0000, 0x8800}, +/* 206 */ {0x000e, 0x8801}, +/* 207 */ {0x0004, 0x8800}, +/* 209 */ {0x000f, 0x8801}, +/* 210 */ {0x0000, 0x8800}, +/* 212 */ {0x0010, 0x8801}, +/* 213 */ {0x0006, 0x8800}, +/* 215 */ {0x0011, 0x8801}, +/* 216 */ {0x0006, 0x8800}, +/* 218 */ {0x0012, 0x8801}, +/* 219 */ {0x0000, 0x8800}, +/* 221 */ {0x0013, 0x8801}, +/* 222 */ {0x0001, 0x8800}, + +/* 224 */ {0x000a, 0x8700}, +/* 225 */ {0x0000, 0x8702}, +/* 226 */ {0x0000, 0x8703}, +/* 227 */ {0x00c2, 0x8704}, +/* 228 */ {0x0001, 0x870c}, + +/* 229 */ {0x0044, 0x8600}, +/* 230 */ {0x0002, 0x8606}, +/* 231 */ {0x0064, 0x8607}, +/* 232 */ {0x003a, 0x8601}, +/* 233 */ {0x0008, 0x8602}, +/* 234 */ {0x0044, 0x8600}, +/* 235 */ {0x0018, 0x8617}, +/* 236 */ {0x0008, 0x8618}, +/* 237 */ {0x00a1, 0x8656}, +/* 238 */ {0x0004, 0x865b}, +/* 239 */ {0x0002, 0x865c}, +/* 240 */ {0x0058, 0x865d}, +/* 241 */ {0x0048, 0x865e}, +/* 242 */ {0x0012, 0x8608}, +/* 243 */ {0x002c, 0x8609}, +/* 244 */ {0x0002, 0x860a}, +/* 245 */ {0x002c, 0x860b}, +/* 246 */ {0x00db, 0x860c}, +/* 247 */ {0x00f9, 0x860d}, +/* 248 */ {0x00f1, 0x860e}, +/* 249 */ {0x00e3, 0x860f}, +/* 250 */ {0x002c, 0x8610}, +/* 251 */ {0x006c, 0x8651}, +/* 252 */ {0x0041, 0x8652}, +/* 253 */ {0x0059, 0x8653}, +/* 254 */ {0x0040, 0x8654}, +/* 255 */ {0x00fa, 0x8611}, +/* 256 */ {0x00ff, 0x8612}, +/* 257 */ {0x00f8, 0x8613}, +/* 258 */ {0x0000, 0x8614}, +/* 259 */ {0x0001, 0x863f}, +/* 260 */ {0x0000, 0x8640}, +/* 261 */ {0x0026, 0x8641}, +/* 262 */ {0x0045, 0x8642}, +/* 263 */ {0x0060, 0x8643}, +/* 264 */ {0x0075, 0x8644}, +/* 265 */ {0x0088, 0x8645}, +/* 266 */ {0x009b, 0x8646}, +/* 267 */ {0x00b0, 0x8647}, +/* 268 */ {0x00c5, 0x8648}, +/* 269 */ {0x00d2, 0x8649}, +/* 270 */ {0x00dc, 0x864a}, +/* 271 */ {0x00e5, 0x864b}, +/* 272 */ {0x00eb, 0x864c}, +/* 273 */ {0x00f0, 0x864d}, +/* 274 */ {0x00f6, 0x864e}, +/* 275 */ {0x00fa, 0x864f}, +/* 276 */ {0x00ff, 0x8650}, +/* 277 */ {0x0060, 0x8657}, +/* 278 */ {0x0010, 0x8658}, +/* 279 */ {0x0018, 0x8659}, +/* 280 */ {0x0005, 0x865a}, +/* 281 */ {0x0018, 0x8660}, +/* 282 */ {0x0003, 0x8509}, +/* 283 */ {0x0011, 0x850a}, +/* 284 */ {0x0032, 0x850b}, +/* 285 */ {0x0010, 0x850c}, +/* 286 */ {0x0021, 0x850d}, +/* 287 */ {0x0001, 0x8500}, +/* 288 */ {0x0000, 0x8508}, +/* 289 */ {0x0012, 0x8608}, +/* 290 */ {0x002c, 0x8609}, +/* 291 */ {0x0002, 0x860a}, +/* 292 */ {0x0039, 0x860b}, +/* 293 */ {0x00d0, 0x860c}, +/* 294 */ {0x00f7, 0x860d}, +/* 295 */ {0x00ed, 0x860e}, +/* 296 */ {0x00db, 0x860f}, +/* 297 */ {0x0039, 0x8610}, +/* 298 */ {0x0012, 0x8657}, +/* 299 */ {0x000c, 0x8619}, +/* 300 */ {0x0004, 0x861a}, +/* 301 */ {0x00a1, 0x8656}, +/* 302 */ {0x00c8, 0x8615}, +/* 303 */ {0x0032, 0x8616}, + +/* 306 */ {0x0030, 0x8112}, +/* 313 */ {0x0020, 0x8112}, +/* 314 */ {0x0020, 0x8112}, +/* 315 */ {0x000f, 0x8402}, +/* 316 */ {0x0000, 0x8403}, + +/* 317 */ {0x0090, 0x8110}, +/* 318 */ {0x0001, 0x8114}, +/* 319 */ {0x0001, 0x8114}, +/* 320 */ {0x0001, 0x8114}, +/* 321 */ {0x0003, 0x8114}, +/* 322 */ {0x0080, 0x8804}, + +/* 355 */ {0x0003, 0x8801}, +/* 356 */ {0x0012, 0x8800}, +/* 358 */ {0x0004, 0x8801}, +/* 359 */ {0x0005, 0x8800}, +/* 361 */ {0x0005, 0x8801}, +/* 362 */ {0x0047, 0x8800}, +/* 364 */ {0x0006, 0x8801}, +/* 365 */ {0x0000, 0x8800}, +/* 367 */ {0x0007, 0x8801}, +/* 368 */ {0x00c0, 0x8800}, +/* 370 */ {0x0008, 0x8801}, +/* 371 */ {0x0003, 0x8800}, +/* 373 */ {0x000a, 0x8700}, +/* 374 */ {0x000e, 0x8801}, +/* 375 */ {0x0004, 0x8800}, +/* 377 */ {0x0005, 0x8801}, +/* 378 */ {0x0047, 0x8800}, +/* 380 */ {0x0006, 0x8801}, +/* 381 */ {0x0000, 0x8800}, +/* 383 */ {0x0007, 0x8801}, +/* 384 */ {0x00c0, 0x8800}, +/* 386 */ {0x0008, 0x8801}, +/* 387 */ {0x0003, 0x8800}, +/* 389 */ {0x0013, 0x8801}, +/* 390 */ {0x0001, 0x8800}, +/* 392 */ {0x0009, 0x8801}, +/* 393 */ {0x0000, 0x8800}, +/* 395 */ {0x000a, 0x8801}, +/* 396 */ {0x0000, 0x8800}, +/* 398 */ {0x000b, 0x8801}, +/* 399 */ {0x0000, 0x8800}, +/* 401 */ {0x000c, 0x8801}, +/* 402 */ {0x0000, 0x8800}, +/* 404 */ {0x000e, 0x8801}, +/* 405 */ {0x0004, 0x8800}, +/* 407 */ {0x000f, 0x8801}, +/* 408 */ {0x0000, 0x8800}, +/* 410 */ {0x0010, 0x8801}, +/* 411 */ {0x0006, 0x8800}, +/* 413 */ {0x0011, 0x8801}, +/* 414 */ {0x0006, 0x8800}, +/* 416 */ {0x0012, 0x8801}, +/* 417 */ {0x0000, 0x8800}, +/* 419 */ {0x0013, 0x8801}, +/* 420 */ {0x0001, 0x8800}, +/* 422 */ {0x000a, 0x8700}, +/* 423 */ {0x0000, 0x8702}, +/* 424 */ {0x0000, 0x8703}, +/* 425 */ {0x00c2, 0x8704}, +/* 426 */ {0x0001, 0x870c}, +/* 427 */ {0x0044, 0x8600}, +/* 428 */ {0x0002, 0x8606}, +/* 429 */ {0x0064, 0x8607}, +/* 430 */ {0x003a, 0x8601}, +/* 431 */ {0x0008, 0x8602}, +/* 432 */ {0x0044, 0x8600}, +/* 433 */ {0x0018, 0x8617}, +/* 434 */ {0x0008, 0x8618}, +/* 435 */ {0x00a1, 0x8656}, +/* 436 */ {0x0004, 0x865b}, +/* 437 */ {0x0002, 0x865c}, +/* 438 */ {0x0058, 0x865d}, +/* 439 */ {0x0048, 0x865e}, +/* 440 */ {0x0012, 0x8608}, +/* 441 */ {0x002c, 0x8609}, +/* 442 */ {0x0002, 0x860a}, +/* 443 */ {0x002c, 0x860b}, +/* 444 */ {0x00db, 0x860c}, +/* 445 */ {0x00f9, 0x860d}, +/* 446 */ {0x00f1, 0x860e}, +/* 447 */ {0x00e3, 0x860f}, +/* 448 */ {0x002c, 0x8610}, +/* 449 */ {0x006c, 0x8651}, +/* 450 */ {0x0041, 0x8652}, +/* 451 */ {0x0059, 0x8653}, +/* 452 */ {0x0040, 0x8654}, +/* 453 */ {0x00fa, 0x8611}, +/* 454 */ {0x00ff, 0x8612}, +/* 455 */ {0x00f8, 0x8613}, +/* 456 */ {0x0000, 0x8614}, +/* 457 */ {0x0001, 0x863f}, +/* 458 */ {0x0000, 0x8640}, +/* 459 */ {0x0026, 0x8641}, +/* 460 */ {0x0045, 0x8642}, +/* 461 */ {0x0060, 0x8643}, +/* 462 */ {0x0075, 0x8644}, +/* 463 */ {0x0088, 0x8645}, +/* 464 */ {0x009b, 0x8646}, +/* 465 */ {0x00b0, 0x8647}, +/* 466 */ {0x00c5, 0x8648}, +/* 467 */ {0x00d2, 0x8649}, +/* 468 */ {0x00dc, 0x864a}, +/* 469 */ {0x00e5, 0x864b}, +/* 470 */ {0x00eb, 0x864c}, +/* 471 */ {0x00f0, 0x864d}, +/* 472 */ {0x00f6, 0x864e}, +/* 473 */ {0x00fa, 0x864f}, +/* 474 */ {0x00ff, 0x8650}, +/* 475 */ {0x0060, 0x8657}, +/* 476 */ {0x0010, 0x8658}, +/* 477 */ {0x0018, 0x8659}, +/* 478 */ {0x0005, 0x865a}, +/* 479 */ {0x0018, 0x8660}, +/* 480 */ {0x0003, 0x8509}, +/* 481 */ {0x0011, 0x850a}, +/* 482 */ {0x0032, 0x850b}, +/* 483 */ {0x0010, 0x850c}, +/* 484 */ {0x0021, 0x850d}, +/* 485 */ {0x0001, 0x8500}, +/* 486 */ {0x0000, 0x8508}, + +/* 487 */ {0x0012, 0x8608}, +/* 488 */ {0x002c, 0x8609}, +/* 489 */ {0x0002, 0x860a}, +/* 490 */ {0x0039, 0x860b}, +/* 491 */ {0x00d0, 0x860c}, +/* 492 */ {0x00f7, 0x860d}, +/* 493 */ {0x00ed, 0x860e}, +/* 494 */ {0x00db, 0x860f}, +/* 495 */ {0x0039, 0x8610}, +/* 496 */ {0x0012, 0x8657}, +/* 497 */ {0x0064, 0x8619}, + +/* This line starts it all, it is not needed here */ +/* since it has been build into the driver */ +/* jfm: don't start now */ +/* 590 * {0x0030, 0x8112}, */ + {} +}; + +/* + * Initialization data for Creative Webcam Vista + */ +static __u16 spca508_vista_init_data[][3] = { + {0x0008, 0x8200}, /* Clear register */ + {0x0000, 0x870b}, /* Reset CTL3 */ + {0x0020, 0x8112}, /* Video Drop packet enable */ + {0x0003, 0x8111}, /* Soft Reset compression, memory, TG & CDSP */ + {0x0000, 0x8110}, /* Disable everything */ + {0x0000, 0x8114}, /* Software GPIO output data */ + {0x0000, 0x8114}, + + {0x0003, 0x8111}, + {0x0000, 0x8111}, + {0x0090, 0x8110}, /* Enable: SSI output, External 2X clock output */ + {0x0020, 0x8112}, + {0x0000, 0x8114}, + {0x0001, 0x8114}, + {0x0001, 0x8114}, + {0x0001, 0x8114}, + {0x0003, 0x8114}, + + {0x000f, 0x8402}, /* Memory bank Address */ + {0x0000, 0x8403}, /* Memory bank Address */ + {0x00ba, 0x8804}, /* SSI Slave address */ + {0x0010, 0x8802}, /* 93.75kHz SSI Clock Two DataByte */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, /* Will write 2 bytes (DATA1+DATA2) */ + {0x0020, 0x8801}, /* Register address for SSI read/write */ + {0x0044, 0x8805}, /* DATA2 */ + {0x0004, 0x8800}, /* DATA1 -> write triggered */ + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0009, 0x8801}, + {0x0042, 0x8805}, + {0x0001, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x003c, 0x8801}, + {0x0001, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0001, 0x8801}, + {0x000a, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0002, 0x8801}, + {0x0000, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0003, 0x8801}, + {0x0027, 0x8805}, + {0x0001, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0004, 0x8801}, + {0x0065, 0x8805}, + {0x0001, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0005, 0x8801}, + {0x0003, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0006, 0x8801}, + {0x001c, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0007, 0x8801}, + {0x002a, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x000e, 0x8801}, + {0x0000, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0028, 0x8801}, + {0x002e, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0039, 0x8801}, + {0x0013, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x003b, 0x8801}, + {0x000c, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0035, 0x8801}, + {0x0028, 0x8805}, + {0x0000, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + /* READ { 0, 0x0001, 0x8802 } -> + 0000: 10 */ + {0x0010, 0x8802}, + {0x0009, 0x8801}, + {0x0042, 0x8805}, + {0x0001, 0x8800}, + /* READ { 0, 0x0001, 0x8803 } -> + 0000: 00 */ + + {0x0050, 0x8703}, + {0x0002, 0x8704}, /* External input CKIx1 */ + {0x0001, 0x870C}, /* Select CKOx2 output */ + {0x009A, 0x8600}, /* Line memory Read Counter (L) */ + {0x0001, 0x8606}, /* 1 Line memory Read Counter (H) Result: (d)410 */ + {0x0023, 0x8601}, + {0x0010, 0x8602}, + {0x000A, 0x8603}, + {0x009A, 0x8600}, + {0x0001, 0x865B}, /* 1 Horizontal Offset for Valid Pixel(L) */ + {0x0003, 0x865C}, /* Vertical offset for valid lines (L) */ + {0x0058, 0x865D}, /* Horizontal valid pixels window (L) */ + {0x0048, 0x865E}, /* Vertical valid lines window (L) */ + {0x0000, 0x865F}, + + {0x0006, 0x8660}, + /* Enable nibble data input, select nibble input order */ + + {0x0013, 0x8608}, /* A11 Coeficients for color correction */ + {0x0028, 0x8609}, + /* Note: these values are confirmed at the end of array */ + {0x0005, 0x860A}, /* ... */ + {0x0025, 0x860B}, + {0x00E1, 0x860C}, + {0x00FA, 0x860D}, + {0x00F4, 0x860E}, + {0x00E8, 0x860F}, + {0x0025, 0x8610}, /* A33 Coef. */ + {0x00FC, 0x8611}, /* White balance offset: R */ + {0x0001, 0x8612}, /* White balance offset: Gr */ + {0x00FE, 0x8613}, /* White balance offset: B */ + {0x0000, 0x8614}, /* White balance offset: Gb */ + + {0x0064, 0x8651}, /* R gain for white balance (L) */ + {0x0040, 0x8652}, /* Gr gain for white balance (L) */ + {0x0066, 0x8653}, /* B gain for white balance (L) */ + {0x0040, 0x8654}, /* Gb gain for white balance (L) */ + {0x0001, 0x863F}, /* Enable fixed gamma correction */ + + {0x00A1, 0x8656}, /* Size - Window1: 256x256, Window2: 128x128 */ + /* UV division: UV no change, Enable New edge enhancement */ + {0x0018, 0x8657}, /* Edge gain high threshold */ + {0x0020, 0x8658}, /* Edge gain low threshold */ + {0x000A, 0x8659}, /* Edge bandwidth high threshold */ + {0x0005, 0x865A}, /* Edge bandwidth low threshold */ + {0x0064, 0x8607}, /* UV filter enable */ + + {0x0016, 0x8660}, + {0x0000, 0x86B0}, /* Bad pixels compensation address */ + {0x00DC, 0x86B1}, /* X coord for bad pixels compensation (L) */ + {0x0000, 0x86B2}, + {0x0009, 0x86B3}, /* Y coord for bad pixels compensation (L) */ + {0x0000, 0x86B4}, + + {0x0001, 0x86B0}, + {0x00F5, 0x86B1}, + {0x0000, 0x86B2}, + {0x00C6, 0x86B3}, + {0x0000, 0x86B4}, + + {0x0002, 0x86B0}, + {0x001C, 0x86B1}, + {0x0001, 0x86B2}, + {0x00D7, 0x86B3}, + {0x0000, 0x86B4}, + + {0x0003, 0x86B0}, + {0x001C, 0x86B1}, + {0x0001, 0x86B2}, + {0x00D8, 0x86B3}, + {0x0000, 0x86B4}, + + {0x0004, 0x86B0}, + {0x001D, 0x86B1}, + {0x0001, 0x86B2}, + {0x00D8, 0x86B3}, + {0x0000, 0x86B4}, + {0x001E, 0x8660}, + + /* READ { 0, 0x0000, 0x8608 } -> + 0000: 13 */ + /* READ { 0, 0x0000, 0x8609 } -> + 0000: 28 */ + /* READ { 0, 0x0000, 0x8610 } -> + 0000: 05 */ + /* READ { 0, 0x0000, 0x8611 } -> + 0000: 25 */ + /* READ { 0, 0x0000, 0x8612 } -> + 0000: e1 */ + /* READ { 0, 0x0000, 0x8613 } -> + 0000: fa */ + /* READ { 0, 0x0000, 0x8614 } -> + 0000: f4 */ + /* READ { 0, 0x0000, 0x8615 } -> + 0000: e8 */ + /* READ { 0, 0x0000, 0x8616 } -> + 0000: 25 */ + {} +}; + +static int reg_write(struct usb_device *dev, + __u16 index, __u16 value) +{ + int ret; + + ret = usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + 0, /* request */ + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + PDEBUG(D_USBO, "reg write i:0x%04x = 0x%02x", + index, value); + if (ret < 0) + PDEBUG(D_ERR|D_USBO, "reg write: error %d", ret); + return ret; +} + +/* read 1 byte */ +/* returns: negative is error, pos or zero is data */ +static int reg_read(struct usb_device *dev, + __u16 index) /* wIndex */ +{ + int ret; + __u8 data; + + ret = usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + 0, /* register */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + (__u16) 0, /* value */ + index, + &data, 1, + 500); /* timeout */ + PDEBUG(D_USBI, "reg read i:%04x --> %02x", index, data); + if (ret < 0) { + PDEBUG(D_ERR|D_USBI, "reg_read err %d", ret); + return ret; + } + return data; +} + +static int write_vector(struct gspca_dev *gspca_dev, + __u16 data[][3]) +{ + struct usb_device *dev = gspca_dev->dev; + int ret, i = 0; + + while (data[i][1] != 0) { + ret = reg_write(dev, data[i][1], data[i][0]); + if (ret < 0) + return ret; + i++; + } + return 0; +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + struct cam *cam; + __u16 vendor; + __u16 product; + int data1, data2; + + vendor = id->idVendor; + product = id->idProduct; + switch (vendor) { + case 0x041e: /* Creative cameras */ +/* switch (product) { */ +/* case 0x4018: */ + sd->subtype = CreativeVista; +/* break; */ +/* } */ + break; + case 0x0461: /* MicroInnovation */ +/* switch (product) { */ +/* case 0x0815: */ + sd->subtype = MicroInnovationIC200; +/* break; */ +/* } */ + break; + case 0x0733: /* Rebadged ViewQuest (Intel) and ViewQuest cameras */ +/* switch (product) { */ +/* case 0x110: */ + sd->subtype = ViewQuestVQ110; +/* break; */ +/* } */ + break; + case 0x0af9: /* Hama cameras */ + switch (product) { + case 0x0010: + sd->subtype = HamaUSBSightcam; + break; + case 0x0011: + sd->subtype = HamaUSBSightcam2; + break; + } + break; + case 0x8086: /* Intel */ +/* switch (product) { */ +/* case 0x0110: */ + sd->subtype = IntelEasyPCCamera; +/* break; */ +/* } */ + break; + } + + /* Read from global register the USB product and vendor IDs, just to */ + /* prove that we can communicate with the device. This works, which */ + /* confirms at we are communicating properly and that the device */ + /* is a 508. */ + data1 = reg_read(dev, 0x8104); + data2 = reg_read(dev, 0x8105); + PDEBUG(D_PROBE, + "Read from GLOBAL: USB Vendor ID 0x%02x%02x", data2, data1); + + data1 = reg_read(dev, 0x8106); + data2 = reg_read(dev, 0x8107); + PDEBUG(D_PROBE, + "Read from GLOBAL: USB Product ID 0x%02x%02x", data2, data1); + + data1 = reg_read(dev, 0x8621); + PDEBUG(D_PROBE, + "Read from GLOBAL: Window 1 average luminance %d", data1); + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + cam->cam_mode = sif_mode; + cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + + switch (sd->subtype) { + case ViewQuestVQ110: + if (write_vector(gspca_dev, spca508_init_data)) + return -1; + break; + default: +/* case MicroInnovationIC200: */ +/* case IntelEasyPCCamera: */ + if (write_vector(gspca_dev, spca508cs110_init_data)) + return -1; + break; + case HamaUSBSightcam: + if (write_vector(gspca_dev, spca508_sightcam_init_data)) + return -1; + break; + case HamaUSBSightcam2: + if (write_vector(gspca_dev, spca508_sightcam2_init_data)) + return -1; + break; + case CreativeVista: + if (write_vector(gspca_dev, spca508_vista_init_data)) + return -1; + break; + } + return 0; /* success */ +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ +/* write_vector(gspca_dev, spca508_open_data); */ + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + int mode; + + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode; + reg_write(gspca_dev->dev, 0x8500, mode); + switch (mode) { + case 0: + case 1: + reg_write(gspca_dev->dev, 0x8700, 0x28); /* clock */ + break; + default: +/* case 2: */ +/* case 3: */ + reg_write(gspca_dev->dev, 0x8700, 0x23); /* clock */ + break; + } + reg_write(gspca_dev->dev, 0x8112, 0x10 | 0x20); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + /* Video ISO disable, Video Drop Packet enable: */ + reg_write(gspca_dev->dev, 0x8112, 0x20); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +/* this function is called at close time */ +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +/* convert YUVY per line to YUYV (YUV 4:2:2) */ +static void yuvy_decode(unsigned char *out, + unsigned char *in, + int width, + int height) +{ + unsigned char *Ui, *Vi, *yi, *yi1; + unsigned char *out1; + int i, j; + + yi = in; + for (i = height / 2; --i >= 0; ) { + out1 = out + width * 2; /* next line */ + Ui = yi + width; + Vi = Ui + width / 2; + yi1 = Vi + width / 2; + for (j = width / 2; --j >= 0; ) { + *out++ = 128 + *yi++; + *out++ = 128 + *Ui; + *out++ = 128 + *yi++; + *out++ = 128 + *Vi; + + *out1++ = 128 + *yi1++; + *out1++ = 128 + *Ui++; + *out1++ = 128 + *yi1++; + *out1++ = 128 + *Vi++; + } + yi += width * 2; + out = out1; + } +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + unsigned char *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (data[0]) { + case 0: /* start of frame */ + if (gspca_dev->last_packet_type == FIRST_PACKET) { + yuvy_decode(sd->tmpbuf2, sd->tmpbuf, + gspca_dev->width, + gspca_dev->height); + frame = gspca_frame_add(gspca_dev, + LAST_PACKET, + frame, + sd->tmpbuf2, + gspca_dev->width + * gspca_dev->height + * 2); + } + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, 0); + data += SPCA508_OFFSET_DATA; + len -= SPCA508_OFFSET_DATA; + if (len > 0) + memcpy(sd->tmpbuf, data, len); + else + len = 0; + sd->buflen = len; + return; + case 0xff: /* drop */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + data += 1; + len -= 1; + memcpy(&sd->tmpbuf[sd->buflen], data, len); + sd->buflen += len; +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 brightness = sd->brightness; + +/* MX seem contrast */ + reg_write(gspca_dev->dev, 0x8651, brightness); + reg_write(gspca_dev->dev, 0x8652, brightness); + reg_write(gspca_dev->dev, 0x8653, brightness); + reg_write(gspca_dev->dev, 0x8654, brightness); +} + +static void getbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = reg_read(gspca_dev->dev, 0x8651); +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x041e, 0x4018), DVNM("Creative Webcam Vista (PD1100)")}, + {USB_DEVICE(0x0461, 0x0815), DVNM("Micro Innovation IC200")}, + {USB_DEVICE(0x0733, 0x0110), DVNM("ViewQuest VQ110")}, + {USB_DEVICE(0x0af9, 0x0010), DVNM("Hama USB Sightcam 100")}, + {USB_DEVICE(0x0af9, 0x0011), DVNM("Hama USB Sightcam 100")}, + {USB_DEVICE(0x8086, 0x0110), DVNM("Intel Easy PC Camera")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c new file mode 100644 index 000000000000..a94e6270115e --- /dev/null +++ b/drivers/media/video/gspca/spca561.c @@ -0,0 +1,1025 @@ +/* + * Sunplus spca561 subdriver + * + * Copyright (C) 2004 Michel Xhaard mxhaard@magic.fr + * + * V4L2 by Jean-Francois Moine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "spca561" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/SPCA561 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + unsigned short contrast; + __u8 brightness; + __u8 autogain; + + __u8 chip_revision; + signed char ag_cnt; +#define AG_CNT_START 13 +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 63, + .step = 1, + .default_value = 32, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 0x3fff, + .step = 1, + .default_value = 0x2000, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define SD_AUTOGAIN 2 + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +}; + +static struct cam_mode sif_mode[] = { + {V4L2_PIX_FMT_SPCA561, 160, 120, 3}, + {V4L2_PIX_FMT_SPCA561, 176, 144, 2}, + {V4L2_PIX_FMT_SPCA561, 320, 240, 1}, + {V4L2_PIX_FMT_SPCA561, 352, 288, 0}, +}; + +/* + * Initialization data + * I'm not very sure how to split initialization from open data + * chunks. For now, we'll consider everything as initialization + */ +/* Frame packet header offsets for the spca561 */ +#define SPCA561_OFFSET_SNAP 1 +#define SPCA561_OFFSET_TYPE 2 +#define SPCA561_OFFSET_COMPRESS 3 +#define SPCA561_OFFSET_FRAMSEQ 4 +#define SPCA561_OFFSET_GPIO 5 +#define SPCA561_OFFSET_USBBUFF 6 +#define SPCA561_OFFSET_WIN2GRAVE 7 +#define SPCA561_OFFSET_WIN2RAVE 8 +#define SPCA561_OFFSET_WIN2BAVE 9 +#define SPCA561_OFFSET_WIN2GBAVE 10 +#define SPCA561_OFFSET_WIN1GRAVE 11 +#define SPCA561_OFFSET_WIN1RAVE 12 +#define SPCA561_OFFSET_WIN1BAVE 13 +#define SPCA561_OFFSET_WIN1GBAVE 14 +#define SPCA561_OFFSET_FREQ 15 +#define SPCA561_OFFSET_VSYNC 16 +#define SPCA561_OFFSET_DATA 1 +#define SPCA561_INDEX_I2C_BASE 0x8800 +#define SPCA561_SNAPBIT 0x20 +#define SPCA561_SNAPCTRL 0x40 +enum { + Rev072A = 0, + Rev012A, +}; + +static void reg_w_val(struct usb_device *dev, __u16 index, __u16 value) +{ + int ret; + + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0, /* request */ + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + PDEBUG(D_USBO, "reg write: 0x%02x:0x%02x", index, value); + if (ret < 0) + PDEBUG(D_ERR, "reg write: error %d", ret); +} + +static void write_vector(struct gspca_dev *gspca_dev, __u16 data[][2]) +{ + struct usb_device *dev = gspca_dev->dev; + int i; + + i = 0; + while (data[i][1] != 0) { + reg_w_val(dev, data[i][1], data[i][0]); + i++; + } +} + +static void reg_r(struct usb_device *dev, + __u16 index, __u8 *buffer, __u16 length) +{ + usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + 0, /* request */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, buffer, length, 500); +} + +static void reg_w_buf(struct usb_device *dev, + __u16 index, __u8 *buffer, __u16 length) +{ + usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, buffer, length, 500); +} + +static void i2c_init(struct gspca_dev *gspca_dev, __u8 mode) +{ + reg_w_val(gspca_dev->dev, 0x92, 0x8804); + reg_w_val(gspca_dev->dev, mode, 0x8802); +} + +static void i2c_write(struct gspca_dev *gspca_dev, __u16 valeur, __u16 reg) +{ + int retry = 60; + __u8 DataLow; + __u8 DataHight; + __u8 Data; + + DataLow = valeur; + DataHight = valeur >> 8; + reg_w_val(gspca_dev->dev, reg, 0x8801); + reg_w_val(gspca_dev->dev, DataLow, 0x8805); + reg_w_val(gspca_dev->dev, DataHight, 0x8800); + while (retry--) { + reg_r(gspca_dev->dev, 0x8803, &Data, 1); + if (!Data) + break; + } +} + +static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode) +{ + int retry = 60; + __u8 value; + __u8 vallsb; + __u8 Data; + + reg_w_val(gspca_dev->dev, 0x92, 0x8804); + reg_w_val(gspca_dev->dev, reg, 0x8801); + reg_w_val(gspca_dev->dev, (mode | 0x01), 0x8802); + while (retry--) { + reg_r(gspca_dev->dev, 0x8803, &Data, 1); + if (!Data) + break; + } + if (retry == 0) + return -1; + reg_r(gspca_dev->dev, 0x8800, &value, 1); + reg_r(gspca_dev->dev, 0x8805, &vallsb, 1); + return ((int) value << 8) | vallsb; +} + +static __u16 spca561_init_data[][2] = { + {0x0000, 0x8114}, /* Software GPIO output data */ + {0x0001, 0x8114}, /* Software GPIO output data */ + {0x0000, 0x8112}, /* Some kind of reset */ + {0x0003, 0x8701}, /* PCLK clock delay adjustment */ + {0x0001, 0x8703}, /* HSYNC from cmos inverted */ + {0x0011, 0x8118}, /* Enable and conf sensor */ + {0x0001, 0x8118}, /* Conf sensor */ + {0x0092, 0x8804}, /* I know nothing about these */ + {0x0010, 0x8802}, /* 0x88xx registers, so I won't */ + /***************/ + {0x000d, 0x8805}, /* sensor default setting */ + {0x0001, 0x8801}, /* 1 <- 0x0d */ + {0x0000, 0x8800}, + {0x0018, 0x8805}, + {0x0002, 0x8801}, /* 2 <- 0x18 */ + {0x0000, 0x8800}, + {0x0065, 0x8805}, + {0x0004, 0x8801}, /* 4 <- 0x01 0x65 */ + {0x0001, 0x8800}, + {0x0021, 0x8805}, + {0x0005, 0x8801}, /* 5 <- 0x21 */ + {0x0000, 0x8800}, + {0x00aa, 0x8805}, + {0x0007, 0x8801}, /* 7 <- 0xaa */ + {0x0000, 0x8800}, + {0x0004, 0x8805}, + {0x0020, 0x8801}, /* 0x20 <- 0x15 0x04 */ + {0x0015, 0x8800}, + {0x0002, 0x8805}, + {0x0039, 0x8801}, /* 0x39 <- 0x02 */ + {0x0000, 0x8800}, + {0x0010, 0x8805}, + {0x0035, 0x8801}, /* 0x35 <- 0x10 */ + {0x0000, 0x8800}, + {0x0049, 0x8805}, + {0x0009, 0x8801}, /* 0x09 <- 0x10 0x49 */ + {0x0010, 0x8800}, + {0x000b, 0x8805}, + {0x0028, 0x8801}, /* 0x28 <- 0x0b */ + {0x0000, 0x8800}, + {0x000f, 0x8805}, + {0x003b, 0x8801}, /* 0x3b <- 0x0f */ + {0x0000, 0x8800}, + {0x0000, 0x8805}, + {0x003c, 0x8801}, /* 0x3c <- 0x00 */ + {0x0000, 0x8800}, + /***************/ + {0x0018, 0x8601}, /* Pixel/line selection for color separation */ + {0x0000, 0x8602}, /* Optical black level for user setting */ + {0x0060, 0x8604}, /* Optical black horizontal offset */ + {0x0002, 0x8605}, /* Optical black vertical offset */ + {0x0000, 0x8603}, /* Non-automatic optical black level */ + {0x0002, 0x865b}, /* Horizontal offset for valid pixels */ + {0x0000, 0x865f}, /* Vertical valid pixels window (x2) */ + {0x00b0, 0x865d}, /* Horizontal valid pixels window (x2) */ + {0x0090, 0x865e}, /* Vertical valid lines window (x2) */ + {0x00e0, 0x8406}, /* Memory buffer threshold */ + {0x0000, 0x8660}, /* Compensation memory stuff */ + {0x0002, 0x8201}, /* Output address for r/w serial EEPROM */ + {0x0008, 0x8200}, /* Clear valid bit for serial EEPROM */ + {0x0001, 0x8200}, /* OprMode to be executed by hardware */ + {0x0007, 0x8201}, /* Output address for r/w serial EEPROM */ + {0x0008, 0x8200}, /* Clear valid bit for serial EEPROM */ + {0x0001, 0x8200}, /* OprMode to be executed by hardware */ + {0x0010, 0x8660}, /* Compensation memory stuff */ + {0x0018, 0x8660}, /* Compensation memory stuff */ + + {0x0004, 0x8611}, /* R offset for white balance */ + {0x0004, 0x8612}, /* Gr offset for white balance */ + {0x0007, 0x8613}, /* B offset for white balance */ + {0x0000, 0x8614}, /* Gb offset for white balance */ + {0x008c, 0x8651}, /* R gain for white balance */ + {0x008c, 0x8652}, /* Gr gain for white balance */ + {0x00b5, 0x8653}, /* B gain for white balance */ + {0x008c, 0x8654}, /* Gb gain for white balance */ + {0x0002, 0x8502}, /* Maximum average bit rate stuff */ + + {0x0011, 0x8802}, + {0x0087, 0x8700}, /* Set master clock (96Mhz????) */ + {0x0081, 0x8702}, /* Master clock output enable */ + + {0x0000, 0x8500}, /* Set image type (352x288 no compression) */ + /* Originally was 0x0010 (352x288 compression) */ + + {0x0002, 0x865b}, /* Horizontal offset for valid pixels */ + {0x0003, 0x865c}, /* Vertical offset for valid lines */ + /***************//* sensor active */ + {0x0003, 0x8801}, /* 0x03 <- 0x01 0x21 //289 */ + {0x0021, 0x8805}, + {0x0001, 0x8800}, + {0x0004, 0x8801}, /* 0x04 <- 0x01 0x65 //357 */ + {0x0065, 0x8805}, + {0x0001, 0x8800}, + {0x0005, 0x8801}, /* 0x05 <- 0x2f */ + {0x002f, 0x8805}, + {0x0000, 0x8800}, + {0x0006, 0x8801}, /* 0x06 <- 0 */ + {0x0000, 0x8805}, + {0x0000, 0x8800}, + {0x000a, 0x8801}, /* 0x0a <- 2 */ + {0x0002, 0x8805}, + {0x0000, 0x8800}, + {0x0009, 0x8801}, /* 0x09 <- 0x1061 */ + {0x0061, 0x8805}, + {0x0010, 0x8800}, + {0x0035, 0x8801}, /* 0x35 <-0x14 */ + {0x0014, 0x8805}, + {0x0000, 0x8800}, + {0x0030, 0x8112}, /* ISO and drop packet enable */ + {0x0000, 0x8112}, /* Some kind of reset ???? */ + {0x0009, 0x8118}, /* Enable sensor and set standby */ + {0x0000, 0x8114}, /* Software GPIO output data */ + {0x0000, 0x8114}, /* Software GPIO output data */ + {0x0001, 0x8114}, /* Software GPIO output data */ + {0x0000, 0x8112}, /* Some kind of reset ??? */ + {0x0003, 0x8701}, + {0x0001, 0x8703}, + {0x0011, 0x8118}, + {0x0001, 0x8118}, + /***************/ + {0x0092, 0x8804}, + {0x0010, 0x8802}, + {0x000d, 0x8805}, + {0x0001, 0x8801}, + {0x0000, 0x8800}, + {0x0018, 0x8805}, + {0x0002, 0x8801}, + {0x0000, 0x8800}, + {0x0065, 0x8805}, + {0x0004, 0x8801}, + {0x0001, 0x8800}, + {0x0021, 0x8805}, + {0x0005, 0x8801}, + {0x0000, 0x8800}, + {0x00aa, 0x8805}, + {0x0007, 0x8801}, /* mode 0xaa */ + {0x0000, 0x8800}, + {0x0004, 0x8805}, + {0x0020, 0x8801}, + {0x0015, 0x8800}, /* mode 0x0415 */ + {0x0002, 0x8805}, + {0x0039, 0x8801}, + {0x0000, 0x8800}, + {0x0010, 0x8805}, + {0x0035, 0x8801}, + {0x0000, 0x8800}, + {0x0049, 0x8805}, + {0x0009, 0x8801}, + {0x0010, 0x8800}, + {0x000b, 0x8805}, + {0x0028, 0x8801}, + {0x0000, 0x8800}, + {0x000f, 0x8805}, + {0x003b, 0x8801}, + {0x0000, 0x8800}, + {0x0000, 0x8805}, + {0x003c, 0x8801}, + {0x0000, 0x8800}, + {0x0002, 0x8502}, + {0x0039, 0x8801}, + {0x0000, 0x8805}, + {0x0000, 0x8800}, + + {0x0087, 0x8700}, /* overwrite by start */ + {0x0081, 0x8702}, + {0x0000, 0x8500}, +/* {0x0010, 0x8500}, -- Previous line was this */ + {0x0002, 0x865b}, + {0x0003, 0x865c}, + /***************/ + {0x0003, 0x8801}, /* 0x121-> 289 */ + {0x0021, 0x8805}, + {0x0001, 0x8800}, + {0x0004, 0x8801}, /* 0x165 -> 357 */ + {0x0065, 0x8805}, + {0x0001, 0x8800}, + {0x0005, 0x8801}, /* 0x2f //blanking control colonne */ + {0x002f, 0x8805}, + {0x0000, 0x8800}, + {0x0006, 0x8801}, /* 0x00 //blanking mode row */ + {0x0000, 0x8805}, + {0x0000, 0x8800}, + {0x000a, 0x8801}, /* 0x01 //0x02 */ + {0x0001, 0x8805}, + {0x0000, 0x8800}, + {0x0009, 0x8801}, /* 0x1061 - setexposure times && pixel clock + * 0001 0 | 000 0110 0001 */ + {0x0061, 0x8805}, /* 61 31 */ + {0x0008, 0x8800}, /* 08 */ + {0x0035, 0x8801}, /* 0x14 - set gain general */ + {0x001f, 0x8805}, /* 0x14 */ + {0x0000, 0x8800}, + {0x0030, 0x8112}, + {} +}; + +static void sensor_reset(struct gspca_dev *gspca_dev) +{ + reg_w_val(gspca_dev->dev, 0x8631, 0xc8); + reg_w_val(gspca_dev->dev, 0x8634, 0xc8); + reg_w_val(gspca_dev->dev, 0x8112, 0x00); + reg_w_val(gspca_dev->dev, 0x8114, 0x00); + reg_w_val(gspca_dev->dev, 0x8118, 0x21); + i2c_init(gspca_dev, 0x14); + i2c_write(gspca_dev, 1, 0x0d); + i2c_write(gspca_dev, 0, 0x0d); +} + +/******************** QC Express etch2 stuff ********************/ +static __u16 Pb100_1map8300[][2] = { + /* reg, value */ + {0x8320, 0x3304}, + + {0x8303, 0x0125}, /* image area */ + {0x8304, 0x0169}, + {0x8328, 0x000b}, + {0x833c, 0x0001}, + + {0x832f, 0x0419}, + {0x8307, 0x00aa}, + {0x8301, 0x0003}, + {0x8302, 0x000e}, + {} +}; +static __u16 Pb100_2map8300[][2] = { + /* reg, value */ + {0x8339, 0x0000}, + {0x8307, 0x00aa}, + {} +}; + +static __u16 spca561_161rev12A_data1[][2] = { + {0x21, 0x8118}, + {0x01, 0x8114}, + {0x00, 0x8112}, + {0x92, 0x8804}, + {0x04, 0x8802}, /* windows uses 08 */ + {} +}; +static __u16 spca561_161rev12A_data2[][2] = { + {0x21, 0x8118}, + {0x10, 0x8500}, + {0x07, 0x8601}, + {0x07, 0x8602}, + {0x04, 0x8501}, + {0x21, 0x8118}, + + {0x07, 0x8201}, /* windows uses 02 */ + {0x08, 0x8200}, + {0x01, 0x8200}, + + {0x00, 0x8114}, + {0x01, 0x8114}, /* windows uses 00 */ + + {0x90, 0x8604}, + {0x00, 0x8605}, + {0xb0, 0x8603}, + + /* sensor gains */ + {0x00, 0x8610}, /* *red */ + {0x00, 0x8611}, /* 3f *green */ + {0x00, 0x8612}, /* green *blue */ + {0x00, 0x8613}, /* blue *green */ + {0x35, 0x8614}, /* green *red */ + {0x35, 0x8615}, /* 40 *green */ + {0x35, 0x8616}, /* 7a *blue */ + {0x35, 0x8617}, /* 40 *green */ + + {0x0c, 0x8620}, /* 0c */ + {0xc8, 0x8631}, /* c8 */ + {0xc8, 0x8634}, /* c8 */ + {0x23, 0x8635}, /* 23 */ + {0x1f, 0x8636}, /* 1f */ + {0xdd, 0x8637}, /* dd */ + {0xe1, 0x8638}, /* e1 */ + {0x1d, 0x8639}, /* 1d */ + {0x21, 0x863a}, /* 21 */ + {0xe3, 0x863b}, /* e3 */ + {0xdf, 0x863c}, /* df */ + {0xf0, 0x8505}, + {0x32, 0x850a}, + {} +}; + +static void sensor_mapwrite(struct gspca_dev *gspca_dev, + __u16 sensormap[][2]) +{ + int i = 0; + __u8 usbval[2]; + + while (sensormap[i][0]) { + usbval[0] = sensormap[i][1]; + usbval[1] = sensormap[i][1] >> 8; + reg_w_buf(gspca_dev->dev, sensormap[i][0], usbval, 2); + i++; + } +} +static void init_161rev12A(struct gspca_dev *gspca_dev) +{ + sensor_reset(gspca_dev); + write_vector(gspca_dev, spca561_161rev12A_data1); + sensor_mapwrite(gspca_dev, Pb100_1map8300); + write_vector(gspca_dev, spca561_161rev12A_data2); + sensor_mapwrite(gspca_dev, Pb100_2map8300); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + struct cam *cam; + __u16 vendor, product; + __u8 data1, data2; + + /* Read frm global register the USB product and vendor IDs, just to + * prove that we can communicate with the device. This works, which + * confirms at we are communicating properly and that the device + * is a 561. */ + reg_r(dev, 0x8104, &data1, 1); + reg_r(dev, 0x8105, &data2, 1); + vendor = (data2 << 8) | data1; + reg_r(dev, 0x8106, &data1, 1); + reg_r(dev, 0x8107, &data2, 1); + product = (data2 << 8) | data1; + if (vendor != id->idVendor || product != id->idProduct) { + PDEBUG(D_PROBE, "Bad vendor / product from device"); + return -EINVAL; + } + switch (product) { + case 0x0928: + case 0x0929: + case 0x092a: + case 0x092b: + case 0x092c: + case 0x092d: + case 0x092e: + case 0x092f: + case 0x403b: + sd->chip_revision = Rev012A; + break; + default: +/* case 0x0561: + case 0x0815: * ?? in spca508.c + case 0x401a: + case 0x7004: + case 0x7e50: + case 0xa001: + case 0xcdee: */ + sd->chip_revision = Rev072A; + break; + } + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + gspca_dev->nbalt = 7 + 1; /* choose alternate 7 first */ + cam->cam_mode = sif_mode; + cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; + sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value; + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->chip_revision) { + case Rev072A: + PDEBUG(D_STREAM, "Chip revision id: 072a"); + write_vector(gspca_dev, spca561_init_data); + break; + default: +/* case Rev012A: */ + PDEBUG(D_STREAM, "Chip revision id: 012a"); + init_161rev12A(gspca_dev); + break; + } + return 0; +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + __u8 lowb; + int expotimes; + + switch (sd->chip_revision) { + case Rev072A: + lowb = sd->contrast >> 8; + reg_w_val(dev, lowb, 0x8651); + reg_w_val(dev, lowb, 0x8652); + reg_w_val(dev, lowb, 0x8653); + reg_w_val(dev, lowb, 0x8654); + break; + case Rev012A: { + __u8 Reg8391[] = + { 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00 }; + + /* Write camera sensor settings */ + expotimes = (sd->contrast >> 5) & 0x07ff; + Reg8391[0] = expotimes & 0xff; /* exposure */ + Reg8391[1] = 0x18 | (expotimes >> 8); + Reg8391[2] = sd->brightness; /* gain */ + reg_w_buf(dev, 0x8391, Reg8391, 8); + reg_w_buf(dev, 0x8390, Reg8391, 8); + break; + } + } +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + int Clck; + __u8 Reg8307[] = { 0xaa, 0x00 }; + int mode; + + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode; + switch (sd->chip_revision) { + case Rev072A: + switch (mode) { + default: +/* case 0: + case 1: */ + Clck = 0x25; + break; + case 2: + Clck = 0x22; + break; + case 3: + Clck = 0x21; + break; + } + reg_w_val(dev, 0x8500, mode); /* mode */ + reg_w_val(dev, 0x8700, Clck); /* 0x27 clock */ + reg_w_val(dev, 0x8112, 0x10 | 0x20); + break; + default: +/* case Rev012A: */ + switch (mode) { + case 0: + case 1: + Clck = 0x8a; + break; + case 2: + Clck = 0x85; + break; + default: + Clck = 0x83; + break; + } + if (mode <= 1) { + /* Use compression on 320x240 and above */ + reg_w_val(dev, 0x8500, 0x10 | mode); + } else { + /* I couldn't get the compression to work below 320x240 + * Fortunately at these resolutions the bandwidth + * is sufficient to push raw frames at ~20fps */ + reg_w_val(dev, 0x8500, mode); + } /* -- qq@kuku.eu.org */ + reg_w_buf(dev, 0x8307, Reg8307, 2); + reg_w_val(dev, 0x8700, Clck); /* 0x8f 0x85 0x27 clock */ + reg_w_val(dev, 0x8112, 0x1e | 0x20); + reg_w_val(dev, 0x850b, 0x03); + setcontrast(gspca_dev); + break; + } +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + reg_w_val(gspca_dev->dev, 0x8112, 0x20); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +/* this function is called at close time */ +static void sd_close(struct gspca_dev *gspca_dev) +{ + reg_w_val(gspca_dev->dev, 0x8114, 0); +} + +static void setautogain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int expotimes = 0; + int pixelclk = 0; + int gainG = 0; + __u8 R, Gr, Gb, B; + int y; + __u8 luma_mean = 110; + __u8 luma_delta = 20; + __u8 spring = 4; + + switch (sd->chip_revision) { + case Rev072A: + reg_r(gspca_dev->dev, 0x8621, &Gr, 1); + reg_r(gspca_dev->dev, 0x8622, &R, 1); + reg_r(gspca_dev->dev, 0x8623, &B, 1); + reg_r(gspca_dev->dev, 0x8624, &Gb, 1); + y = (77 * R + 75 * (Gr + Gb) + 29 * B) >> 8; + /* u= (128*B-(43*(Gr+Gb+R))) >> 8; */ + /* v= (128*R-(53*(Gr+Gb))-21*B) >> 8; */ + /* PDEBUG(D_CONF,"reading Y %d U %d V %d ",y,u,v); */ + + if (y < luma_mean - luma_delta || + y > luma_mean + luma_delta) { + expotimes = i2c_read(gspca_dev, 0x09, 0x10); + pixelclk = 0x0800; + expotimes = expotimes & 0x07ff; + /* PDEBUG(D_PACK, + "Exposition Times 0x%03X Clock 0x%04X ", + expotimes,pixelclk); */ + gainG = i2c_read(gspca_dev, 0x35, 0x10); + /* PDEBUG(D_PACK, + "reading Gain register %d", gainG); */ + + expotimes += (luma_mean - y) >> spring; + gainG += (luma_mean - y) / 50; + /* PDEBUG(D_PACK, + "compute expotimes %d gain %d", + expotimes,gainG); */ + + if (gainG > 0x3f) + gainG = 0x3f; + else if (gainG < 4) + gainG = 3; + i2c_write(gspca_dev, gainG, 0x35); + + if (expotimes >= 0x0256) + expotimes = 0x0256; + else if (expotimes < 4) + expotimes = 3; + i2c_write(gspca_dev, expotimes | pixelclk, 0x09); + } + break; + case Rev012A: + /* sensor registers is access and memory mapped to 0x8300 */ + /* readind all 0x83xx block the sensor */ + /* + * The data from the header seem wrong where is the luma + * and chroma mean value + * at the moment set exposure in contrast set + */ + break; + } +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (data[0]) { + case 0: /* start of frame */ + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, 0); + if (sd->ag_cnt >= 0) { + if (--sd->ag_cnt < 0) { + sd->ag_cnt = AG_CNT_START; + setautogain(gspca_dev); + } + } + data += SPCA561_OFFSET_DATA; + len -= SPCA561_OFFSET_DATA; + if (data[1] & 0x10) { + /* compressed bayer */ + gspca_frame_add(gspca_dev, FIRST_PACKET, + frame, data, len); + } else { + /*fixme: which format?*/ + data += 20; + len -= 20; + gspca_frame_add(gspca_dev, FIRST_PACKET, + frame, data, len); + } + return; + case 0xff: /* drop */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + data++; + len--; + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 value; + + switch (sd->chip_revision) { + case Rev072A: + value = sd->brightness; + reg_w_val(gspca_dev->dev, value, 0x8611); + reg_w_val(gspca_dev->dev, value, 0x8612); + reg_w_val(gspca_dev->dev, value, 0x8613); + reg_w_val(gspca_dev->dev, value, 0x8614); + break; + default: +/* case Rev012A: */ + setcontrast(gspca_dev); + break; + } +} + +static void getbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 value; + __u16 tot; + + switch (sd->chip_revision) { + case Rev072A: + tot = 0; + reg_r(gspca_dev->dev, 0x8611, &value, 1); + tot += value; + reg_r(gspca_dev->dev, 0x8612, &value, 1); + tot += value; + reg_r(gspca_dev->dev, 0x8613, &value, 1); + tot += value; + reg_r(gspca_dev->dev, 0x8614, &value, 1); + tot += value; + sd->brightness = tot >> 2; + break; + default: +/* case Rev012A: */ + /* no way to read sensor settings */ + break; + } +} + +static void getcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 value; + __u16 tot; + + switch (sd->chip_revision) { + case Rev072A: + tot = 0; + reg_r(gspca_dev->dev, 0x8651, &value, 1); + tot += value; + reg_r(gspca_dev->dev, 0x8652, &value, 1); + tot += value; + reg_r(gspca_dev->dev, 0x8653, &value, 1); + tot += value; + reg_r(gspca_dev->dev, 0x8654, &value, 1); + tot += value; + sd->contrast = tot << 6; + break; + default: +/* case Rev012A: */ + /* no way to read sensor settings */ + break; + } + PDEBUG(D_CONF, "get contrast %d", sd->contrast); +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcontrast(gspca_dev); + *val = sd->contrast; + return 0; +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + if (val) + sd->ag_cnt = AG_CNT_START; + else + sd->ag_cnt = -1; + return 0; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x041e, 0x401a), DVNM("Creative Webcam Vista (PD1100)")}, + {USB_DEVICE(0x041e, 0x403b), DVNM("Creative Webcam Vista (VF0010)")}, + {USB_DEVICE(0x0458, 0x7004), DVNM("Genius VideoCAM Express V2")}, + {USB_DEVICE(0x046d, 0x0928), DVNM("Logitech QC Express Etch2")}, + {USB_DEVICE(0x046d, 0x0929), DVNM("Labtec Webcam Elch2")}, + {USB_DEVICE(0x046d, 0x092a), DVNM("Logitech QC for Notebook")}, + {USB_DEVICE(0x046d, 0x092b), DVNM("Labtec Webcam Plus")}, + {USB_DEVICE(0x046d, 0x092c), DVNM("Logitech QC chat Elch2")}, + {USB_DEVICE(0x046d, 0x092d), DVNM("Logitech QC Elch2")}, + {USB_DEVICE(0x046d, 0x092e), DVNM("Logitech QC Elch2")}, + {USB_DEVICE(0x046d, 0x092f), DVNM("Logitech QC Elch2")}, + {USB_DEVICE(0x04fc, 0x0561), DVNM("Flexcam 100")}, + {USB_DEVICE(0x060b, 0xa001), DVNM("Maxell Compact Pc PM3")}, + {USB_DEVICE(0x10fd, 0x7e50), DVNM("FlyCam Usb 100")}, + {USB_DEVICE(0xabcd, 0xcdee), DVNM("Petcam")}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c index d8c203e99cd3..6832fe0f3403 100644 --- a/drivers/media/video/gspca/stk014.c +++ b/drivers/media/video/gspca/stk014.c @@ -23,8 +23,8 @@ #include "gspca.h" #include "jpeg.h" -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(0, 2, 7) -static const char version[] = "0.2.7"; +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; MODULE_AUTHOR("Jean-Francois Moine "); MODULE_DESCRIPTION("Syntek DV4000 (STK014) USB Camera Driver"); @@ -37,10 +37,10 @@ struct sd { unsigned char brightness; unsigned char contrast; unsigned char colors; + unsigned char lightfreq; }; /* global parameters */ -static int lightfreq = 50; static int sd_quant = 7; /* <= 4 KO - 7: good (enough!) */ /* V4L2 controls supported by the driver */ @@ -50,6 +50,8 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { #define SD_BRIGHTNESS 0 @@ -94,6 +96,20 @@ static struct ctrl sd_ctrls[] = { .set = sd_setcolors, .get = sd_getcolors, }, +#define SD_FREQ 3 + { + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light frequency filter", + .minimum = 1, + .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ + .step = 1, + .default_value = 1, + }, + .set = sd_setfreq, + .get = sd_getfreq, + }, }; static struct cam_mode vga_mode[] = { @@ -102,11 +118,11 @@ static struct cam_mode vga_mode[] = { }; /* -- read a register -- */ -static int reg_read(struct gspca_dev *gspca_dev, +static int reg_r(struct gspca_dev *gspca_dev, __u16 index, __u8 *buf) { - int ret; struct usb_device *dev = gspca_dev->dev; + int ret; ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0x00, @@ -116,12 +132,12 @@ static int reg_read(struct gspca_dev *gspca_dev, buf, 1, 500); if (ret < 0) - PDEBUG(D_ERR, "reg_read err %d", ret); + PDEBUG(D_ERR, "reg_r err %d", ret); return ret; } /* -- write a register -- */ -static int reg_write(struct gspca_dev *gspca_dev, +static int reg_w(struct gspca_dev *gspca_dev, __u16 index, __u16 value) { struct usb_device *dev = gspca_dev->dev; @@ -136,7 +152,7 @@ static int reg_write(struct gspca_dev *gspca_dev, 0, 500); if (ret < 0) - PDEBUG(D_ERR, "reg_write err %d", ret); + PDEBUG(D_ERR, "reg_w err %d", ret); return ret; } @@ -149,15 +165,15 @@ static int rcv_val(struct gspca_dev *gspca_dev, int alen, ret; unsigned char bulk_buf[4]; - reg_write(gspca_dev, 0x634, (ads >> 16) & 0xff); - reg_write(gspca_dev, 0x635, (ads >> 8) & 0xff); - reg_write(gspca_dev, 0x636, ads & 0xff); - reg_write(gspca_dev, 0x637, 0); - reg_write(gspca_dev, 0x638, len & 0xff); - reg_write(gspca_dev, 0x639, len >> 8); - reg_write(gspca_dev, 0x63a, 0); - reg_write(gspca_dev, 0x63b, 0); - reg_write(gspca_dev, 0x630, 5); + reg_w(gspca_dev, 0x634, (ads >> 16) & 0xff); + reg_w(gspca_dev, 0x635, (ads >> 8) & 0xff); + reg_w(gspca_dev, 0x636, ads & 0xff); + reg_w(gspca_dev, 0x637, 0); + reg_w(gspca_dev, 0x638, len & 0xff); + reg_w(gspca_dev, 0x639, len >> 8); + reg_w(gspca_dev, 0x63a, 0); + reg_w(gspca_dev, 0x63b, 0); + reg_w(gspca_dev, 0x630, 5); if (len > sizeof bulk_buf) return -1; ret = usb_bulk_msg(dev, @@ -180,26 +196,26 @@ static int snd_val(struct gspca_dev *gspca_dev, unsigned char bulk_buf[4]; if (ads == 0x003f08) { - ret = reg_read(gspca_dev, 0x0704, &value); + ret = reg_r(gspca_dev, 0x0704, &value); if (ret < 0) goto ko; - ret = reg_read(gspca_dev, 0x0705, &seq); + ret = reg_r(gspca_dev, 0x0705, &seq); if (ret < 0) goto ko; - ret = reg_read(gspca_dev, 0x0650, &value); + ret = reg_r(gspca_dev, 0x0650, &value); if (ret < 0) goto ko; - reg_write(gspca_dev, 0x654, seq); + reg_w(gspca_dev, 0x654, seq); } else - reg_write(gspca_dev, 0x654, (ads >> 16) & 0xff); - reg_write(gspca_dev, 0x655, (ads >> 8) & 0xff); - reg_write(gspca_dev, 0x656, ads & 0xff); - reg_write(gspca_dev, 0x657, 0); - reg_write(gspca_dev, 0x658, 0x04); /* size */ - reg_write(gspca_dev, 0x659, 0); - reg_write(gspca_dev, 0x65a, 0); - reg_write(gspca_dev, 0x65b, 0); - reg_write(gspca_dev, 0x650, 5); + reg_w(gspca_dev, 0x654, (ads >> 16) & 0xff); + reg_w(gspca_dev, 0x655, (ads >> 8) & 0xff); + reg_w(gspca_dev, 0x656, ads & 0xff); + reg_w(gspca_dev, 0x657, 0); + reg_w(gspca_dev, 0x658, 0x04); /* size */ + reg_w(gspca_dev, 0x659, 0); + reg_w(gspca_dev, 0x65a, 0); + reg_w(gspca_dev, 0x65b, 0); + reg_w(gspca_dev, 0x650, 5); bulk_buf[0] = (val >> 24) & 0xff; bulk_buf[1] = (val >> 16) & 0xff; bulk_buf[2] = (val >> 8) & 0xff; @@ -215,7 +231,7 @@ static int snd_val(struct gspca_dev *gspca_dev, if (ads == 0x003f08) { seq += 4; seq &= 0x3f; - reg_write(gspca_dev, 0x705, seq); + reg_w(gspca_dev, 0x705, seq); } return ret; ko: @@ -235,7 +251,6 @@ static void setbrightness(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; int parval; - PDEBUG(D_CONF, "brightness: %d", sd->brightness); parval = 0x06000000 /* whiteness */ + (sd->brightness << 16); set_par(gspca_dev, parval); @@ -246,7 +261,6 @@ static void setcontrast(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; int parval; - PDEBUG(D_CONF, "contrast: %d", sd->contrast); parval = 0x07000000 /* contrast */ + (sd->contrast << 16); set_par(gspca_dev, parval); @@ -257,13 +271,20 @@ static void setcolors(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; int parval; - PDEBUG(D_CONF, "saturation: %d", - sd->colors); parval = 0x08000000 /* saturation */ + (sd->colors << 16); set_par(gspca_dev, parval); } +static void setfreq(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + set_par(gspca_dev, sd->lightfreq == 1 + ? 0x33640000 /* 50 Hz */ + : 0x33780000); /* 60 Hz */ +} + /* this function is called at probe time */ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) @@ -278,6 +299,7 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value; + sd->lightfreq = sd_ctrls[SD_FREQ].qctrl.default_value; return 0; } @@ -289,7 +311,7 @@ static int sd_open(struct gspca_dev *gspca_dev) /* check if the device responds */ usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1); - ret = reg_read(gspca_dev, 0x0740, &value); + ret = reg_r(gspca_dev, 0x0740, &value); if (ret < 0) return ret; if (value != 0xff) { @@ -320,21 +342,24 @@ static void sd_start(struct gspca_dev *gspca_dev) ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, gspca_dev->alt); - if (ret < 0) + if (ret < 0) { + PDEBUG(D_ERR|D_STREAM, "set intf %d %d failed", + gspca_dev->iface, gspca_dev->alt); goto out; - ret = reg_read(gspca_dev, 0x0630, &dum); + } + ret = reg_r(gspca_dev, 0x0630, &dum); if (ret < 0) goto out; rcv_val(gspca_dev, 0x000020, 4); /* << (value ff ff ff ff) */ - ret = reg_read(gspca_dev, 0x0650, &dum); + ret = reg_r(gspca_dev, 0x0650, &dum); if (ret < 0) goto out; snd_val(gspca_dev, 0x000020, 0xffffffff); - reg_write(gspca_dev, 0x0620, 0); - reg_write(gspca_dev, 0x0630, 0); - reg_write(gspca_dev, 0x0640, 0); - reg_write(gspca_dev, 0x0650, 0); - reg_write(gspca_dev, 0x0660, 0); + reg_w(gspca_dev, 0x0620, 0); + reg_w(gspca_dev, 0x0630, 0); + reg_w(gspca_dev, 0x0640, 0); + reg_w(gspca_dev, 0x0650, 0); + reg_w(gspca_dev, 0x0660, 0); setbrightness(gspca_dev); /* whiteness */ setcontrast(gspca_dev); /* contrast */ setcolors(gspca_dev); /* saturation */ @@ -342,9 +367,7 @@ static void sd_start(struct gspca_dev *gspca_dev) set_par(gspca_dev, 0x0a800000); /* Green ? */ set_par(gspca_dev, 0x0b800000); /* Blue ? */ set_par(gspca_dev, 0x0d030000); /* Gamma ? */ - set_par(gspca_dev, lightfreq == 60 - ? 0x33780000 /* 60 Hz */ - : 0x33640000); /* 50 Hz */ + setfreq(gspca_dev); /* light frequency */ /* start the video flow */ set_par(gspca_dev, 0x01000000); @@ -363,15 +386,15 @@ static void sd_stopN(struct gspca_dev *gspca_dev) set_par(gspca_dev, 0x02000000); set_par(gspca_dev, 0x02000000); usb_set_interface(dev, gspca_dev->iface, 1); - reg_read(gspca_dev, 0x0630, &value); + reg_r(gspca_dev, 0x0630, &value); rcv_val(gspca_dev, 0x000020, 4); /* << (value ff ff ff ff) */ - reg_read(gspca_dev, 0x0650, &value); + reg_r(gspca_dev, 0x0650, &value); snd_val(gspca_dev, 0x000020, 0xffffffff); - reg_write(gspca_dev, 0x0620, 0); - reg_write(gspca_dev, 0x0630, 0); - reg_write(gspca_dev, 0x0640, 0); - reg_write(gspca_dev, 0x0650, 0); - reg_write(gspca_dev, 0x0660, 0); + reg_w(gspca_dev, 0x0620, 0); + reg_w(gspca_dev, 0x0630, 0); + reg_w(gspca_dev, 0x0640, 0); + reg_w(gspca_dev, 0x0650, 0); + reg_w(gspca_dev, 0x0660, 0); PDEBUG(D_STREAM, "camera stopped"); } @@ -470,6 +493,42 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->lightfreq = val; + if (gspca_dev->streaming) + setfreq(gspca_dev); + return 0; +} + +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->lightfreq; + return 0; +} + +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + switch (menu->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (menu->index) { + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + strcpy(menu->name, "50 Hz"); + return 0; + case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ + strcpy(menu->name, "60 Hz"); + return 0; + } + break; + } + return -EINVAL; +} + /* sub-driver description */ static struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -482,6 +541,7 @@ static struct sd_desc sd_desc = { .stop0 = sd_stop0, .close = sd_close, .pkt_scan = sd_pkt_scan, + .querymenu = sd_querymenu, }; /* -- module initialisation -- */ @@ -524,7 +584,5 @@ static void __exit sd_mod_exit(void) module_init(sd_mod_init); module_exit(sd_mod_exit); -module_param(lightfreq, int, 0644); -MODULE_PARM_DESC(lightfreq, "Light frequency 50 or 60 Hz"); module_param_named(quant, sd_quant, int, 0644); MODULE_PARM_DESC(quant, "Quantization index (0..8)"); diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c new file mode 100644 index 000000000000..52d1b32523b1 --- /dev/null +++ b/drivers/media/video/gspca/sunplus.c @@ -0,0 +1,1638 @@ +/* + * Sunplus spca504(abc) spca533 spca536 library + * Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr + * + * V4L2 by Jean-Francois Moine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "sunplus" + +#include "gspca.h" +#include "jpeg.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/SPCA5xx USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + unsigned char packet[ISO_MAX_SIZE + 128]; + /* !! no more than 128 ff in an ISO packet */ + + unsigned char brightness; + unsigned char contrast; + unsigned char colors; + unsigned char autogain; + + char qindex; + char bridge; +#define BRIDGE_SPCA504 0 +#define BRIDGE_SPCA504B 1 +#define BRIDGE_SPCA504C 2 +#define BRIDGE_SPCA533 3 +#define BRIDGE_SPCA536 4 + char subtype; +#define AiptekMiniPenCam13 1 +#define LogitechClickSmart420 2 +#define LogitechClickSmart820 3 +#define MegapixV4 4 +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x20, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define SD_COLOR 2 + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Color", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x1a, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, +#define SD_AUTOGAIN 3 + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +}; + +static struct cam_mode vga_mode[] = { + {V4L2_PIX_FMT_JPEG, 320, 240, 2}, + {V4L2_PIX_FMT_JPEG, 640, 480, 1}, +}; + +static struct cam_mode custom_mode[] = { + {V4L2_PIX_FMT_JPEG, 320, 240, 2}, + {V4L2_PIX_FMT_JPEG, 464, 480, 1}, +}; + +static struct cam_mode vga_mode2[] = { + {V4L2_PIX_FMT_JPEG, 176, 144, 4}, + {V4L2_PIX_FMT_JPEG, 320, 240, 3}, + {V4L2_PIX_FMT_JPEG, 352, 288, 2}, + {V4L2_PIX_FMT_JPEG, 640, 480, 1}, +}; + +#define SPCA50X_OFFSET_DATA 10 +#define SPCA504_PCCAM600_OFFSET_SNAPSHOT 3 +#define SPCA504_PCCAM600_OFFSET_COMPRESS 4 +#define SPCA504_PCCAM600_OFFSET_MODE 5 +#define SPCA504_PCCAM600_OFFSET_DATA 14 + /* Frame packet header offsets for the spca533 */ +#define SPCA533_OFFSET_DATA 16 +#define SPCA533_OFFSET_FRAMSEQ 15 +/* Frame packet header offsets for the spca536 */ +#define SPCA536_OFFSET_DATA 4 +#define SPCA536_OFFSET_FRAMSEQ 1 + +/* Initialisation data for the Creative PC-CAM 600 */ +static __u16 spca504_pccam600_init_data[][3] = { +/* {0xa0, 0x0000, 0x0503}, * capture mode */ + {0x00, 0x0000, 0x2000}, + {0x00, 0x0013, 0x2301}, + {0x00, 0x0003, 0x2000}, + {0x00, 0x0001, 0x21ac}, + {0x00, 0x0001, 0x21a6}, + {0x00, 0x0000, 0x21a7}, /* brightness */ + {0x00, 0x0020, 0x21a8}, /* contrast */ + {0x00, 0x0001, 0x21ac}, /* sat/hue */ + {0x00, 0x0000, 0x21ad}, /* hue */ + {0x00, 0x001a, 0x21ae}, /* saturation */ + {0x00, 0x0002, 0x21a3}, /* gamma */ + {0x30, 0x0154, 0x0008}, + {0x30, 0x0004, 0x0006}, + {0x30, 0x0258, 0x0009}, + {0x30, 0x0004, 0x0000}, + {0x30, 0x0093, 0x0004}, + {0x30, 0x0066, 0x0005}, + {0x00, 0x0000, 0x2000}, + {0x00, 0x0013, 0x2301}, + {0x00, 0x0003, 0x2000}, + {0x00, 0x0013, 0x2301}, + {0x00, 0x0003, 0x2000}, + {} +}; + +/* Creative PC-CAM 600 specific open data, sent before using the + * generic initialisation data from spca504_open_data. + */ +static __u16 spca504_pccam600_open_data[][3] = { + {0x00, 0x0001, 0x2501}, + {0x20, 0x0500, 0x0001}, /* snapshot mode */ + {0x00, 0x0003, 0x2880}, + {0x00, 0x0001, 0x2881}, + {} +}; + +/* Initialisation data for the logitech clicksmart 420 */ +static __u16 spca504A_clicksmart420_init_data[][3] = { +/* {0xa0, 0x0000, 0x0503}, * capture mode */ + {0x00, 0x0000, 0x2000}, + {0x00, 0x0013, 0x2301}, + {0x00, 0x0003, 0x2000}, + {0x00, 0x0001, 0x21ac}, + {0x00, 0x0001, 0x21a6}, + {0x00, 0x0000, 0x21a7}, /* brightness */ + {0x00, 0x0020, 0x21a8}, /* contrast */ + {0x00, 0x0001, 0x21ac}, /* sat/hue */ + {0x00, 0x0000, 0x21ad}, /* hue */ + {0x00, 0x001a, 0x21ae}, /* saturation */ + {0x00, 0x0002, 0x21a3}, /* gamma */ + {0x30, 0x0004, 0x000a}, + {0xb0, 0x0001, 0x0000}, + + + {0x0a1, 0x0080, 0x0001}, + {0x30, 0x0049, 0x0000}, + {0x30, 0x0060, 0x0005}, + {0x0c, 0x0004, 0x0000}, + {0x00, 0x0000, 0x0000}, + {0x00, 0x0000, 0x2000}, + {0x00, 0x0013, 0x2301}, + {0x00, 0x0003, 0x2000}, + {0x00, 0x0000, 0x2000}, + + {} +}; + +/* clicksmart 420 open data ? */ +static __u16 spca504A_clicksmart420_open_data[][3] = { + {0x00, 0x0001, 0x2501}, + {0x20, 0x0502, 0x0000}, + {0x06, 0x0000, 0x0000}, + {0x00, 0x0004, 0x2880}, + {0x00, 0x0001, 0x2881}, +/* look like setting a qTable */ + {0x00, 0x0006, 0x2800}, + {0x00, 0x0004, 0x2801}, + {0x00, 0x0004, 0x2802}, + {0x00, 0x0006, 0x2803}, + {0x00, 0x000a, 0x2804}, + {0x00, 0x0010, 0x2805}, + {0x00, 0x0014, 0x2806}, + {0x00, 0x0018, 0x2807}, + {0x00, 0x0005, 0x2808}, + {0x00, 0x0005, 0x2809}, + {0x00, 0x0006, 0x280a}, + {0x00, 0x0008, 0x280b}, + {0x00, 0x000a, 0x280c}, + {0x00, 0x0017, 0x280d}, + {0x00, 0x0018, 0x280e}, + {0x00, 0x0016, 0x280f}, + + {0x00, 0x0006, 0x2810}, + {0x00, 0x0005, 0x2811}, + {0x00, 0x0006, 0x2812}, + {0x00, 0x000a, 0x2813}, + {0x00, 0x0010, 0x2814}, + {0x00, 0x0017, 0x2815}, + {0x00, 0x001c, 0x2816}, + {0x00, 0x0016, 0x2817}, + {0x00, 0x0006, 0x2818}, + {0x00, 0x0007, 0x2819}, + {0x00, 0x0009, 0x281a}, + {0x00, 0x000c, 0x281b}, + {0x00, 0x0014, 0x281c}, + {0x00, 0x0023, 0x281d}, + {0x00, 0x0020, 0x281e}, + {0x00, 0x0019, 0x281f}, + + {0x00, 0x0007, 0x2820}, + {0x00, 0x0009, 0x2821}, + {0x00, 0x000f, 0x2822}, + {0x00, 0x0016, 0x2823}, + {0x00, 0x001b, 0x2824}, + {0x00, 0x002c, 0x2825}, + {0x00, 0x0029, 0x2826}, + {0x00, 0x001f, 0x2827}, + {0x00, 0x000a, 0x2828}, + {0x00, 0x000e, 0x2829}, + {0x00, 0x0016, 0x282a}, + {0x00, 0x001a, 0x282b}, + {0x00, 0x0020, 0x282c}, + {0x00, 0x002a, 0x282d}, + {0x00, 0x002d, 0x282e}, + {0x00, 0x0025, 0x282f}, + + {0x00, 0x0014, 0x2830}, + {0x00, 0x001a, 0x2831}, + {0x00, 0x001f, 0x2832}, + {0x00, 0x0023, 0x2833}, + {0x00, 0x0029, 0x2834}, + {0x00, 0x0030, 0x2835}, + {0x00, 0x0030, 0x2836}, + {0x00, 0x0028, 0x2837}, + {0x00, 0x001d, 0x2838}, + {0x00, 0x0025, 0x2839}, + {0x00, 0x0026, 0x283a}, + {0x00, 0x0027, 0x283b}, + {0x00, 0x002d, 0x283c}, + {0x00, 0x0028, 0x283d}, + {0x00, 0x0029, 0x283e}, + {0x00, 0x0028, 0x283f}, + + {0x00, 0x0007, 0x2840}, + {0x00, 0x0007, 0x2841}, + {0x00, 0x000a, 0x2842}, + {0x00, 0x0013, 0x2843}, + {0x00, 0x0028, 0x2844}, + {0x00, 0x0028, 0x2845}, + {0x00, 0x0028, 0x2846}, + {0x00, 0x0028, 0x2847}, + {0x00, 0x0007, 0x2848}, + {0x00, 0x0008, 0x2849}, + {0x00, 0x000a, 0x284a}, + {0x00, 0x001a, 0x284b}, + {0x00, 0x0028, 0x284c}, + {0x00, 0x0028, 0x284d}, + {0x00, 0x0028, 0x284e}, + {0x00, 0x0028, 0x284f}, + + {0x00, 0x000a, 0x2850}, + {0x00, 0x000a, 0x2851}, + {0x00, 0x0016, 0x2852}, + {0x00, 0x0028, 0x2853}, + {0x00, 0x0028, 0x2854}, + {0x00, 0x0028, 0x2855}, + {0x00, 0x0028, 0x2856}, + {0x00, 0x0028, 0x2857}, + {0x00, 0x0013, 0x2858}, + {0x00, 0x001a, 0x2859}, + {0x00, 0x0028, 0x285a}, + {0x00, 0x0028, 0x285b}, + {0x00, 0x0028, 0x285c}, + {0x00, 0x0028, 0x285d}, + {0x00, 0x0028, 0x285e}, + {0x00, 0x0028, 0x285f}, + + {0x00, 0x0028, 0x2860}, + {0x00, 0x0028, 0x2861}, + {0x00, 0x0028, 0x2862}, + {0x00, 0x0028, 0x2863}, + {0x00, 0x0028, 0x2864}, + {0x00, 0x0028, 0x2865}, + {0x00, 0x0028, 0x2866}, + {0x00, 0x0028, 0x2867}, + {0x00, 0x0028, 0x2868}, + {0x00, 0x0028, 0x2869}, + {0x00, 0x0028, 0x286a}, + {0x00, 0x0028, 0x286b}, + {0x00, 0x0028, 0x286c}, + {0x00, 0x0028, 0x286d}, + {0x00, 0x0028, 0x286e}, + {0x00, 0x0028, 0x286f}, + + {0x00, 0x0028, 0x2870}, + {0x00, 0x0028, 0x2871}, + {0x00, 0x0028, 0x2872}, + {0x00, 0x0028, 0x2873}, + {0x00, 0x0028, 0x2874}, + {0x00, 0x0028, 0x2875}, + {0x00, 0x0028, 0x2876}, + {0x00, 0x0028, 0x2877}, + {0x00, 0x0028, 0x2878}, + {0x00, 0x0028, 0x2879}, + {0x00, 0x0028, 0x287a}, + {0x00, 0x0028, 0x287b}, + {0x00, 0x0028, 0x287c}, + {0x00, 0x0028, 0x287d}, + {0x00, 0x0028, 0x287e}, + {0x00, 0x0028, 0x287f}, + + {0xa0, 0x0000, 0x0503}, + {} +}; + +static unsigned char qtable_creative_pccam[2][64] = { + { /* Q-table Y-components */ + 0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12, + 0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11, + 0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11, + 0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13, + 0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17, + 0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c, + 0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e, + 0x16, 0x1c, 0x1d, 0x1d, 0x22, 0x1e, 0x1f, 0x1e}, + { /* Q-table C-components */ + 0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, + 0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e} +}; + +/* FIXME: This Q-table is identical to the Creative PC-CAM one, + * except for one byte. Possibly a typo? + * NWG: 18/05/2003. + */ +static unsigned char qtable_spca504_default[2][64] = { + { /* Q-table Y-components */ + 0x05, 0x03, 0x03, 0x05, 0x07, 0x0c, 0x0f, 0x12, + 0x04, 0x04, 0x04, 0x06, 0x08, 0x11, 0x12, 0x11, + 0x04, 0x04, 0x05, 0x07, 0x0c, 0x11, 0x15, 0x11, + 0x04, 0x05, 0x07, 0x09, 0x0f, 0x1a, 0x18, 0x13, + 0x05, 0x07, 0x0b, 0x11, 0x14, 0x21, 0x1f, 0x17, + 0x07, 0x0b, 0x11, 0x13, 0x18, 0x1f, 0x22, 0x1c, + 0x0f, 0x13, 0x17, 0x1a, 0x1f, 0x24, 0x24, 0x1e, + 0x16, 0x1c, 0x1d, 0x1d, 0x1d /* 0x22 */ , 0x1e, 0x1f, 0x1e, + }, + { /* Q-table C-components */ + 0x05, 0x05, 0x07, 0x0e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x05, 0x06, 0x08, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, + 0x07, 0x08, 0x11, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x0e, 0x14, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e} +}; + +static void spca5xxRegRead(struct usb_device *dev, + __u16 req, + __u16 index, + __u8 *buffer, __u16 length) +{ + usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, buffer, length, + 500); +} + +static void spca5xxRegWrite(struct usb_device *dev, + __u16 req, + __u16 value, + __u16 index, + __u8 *buffer, __u16 length) +{ + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, buffer, length, + 500); +} + +static int reg_write(struct usb_device *dev, + __u16 req, __u16 index, __u16 value) +{ + int ret; + + ret = usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + req, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + PDEBUG(D_PACK, "reg write: 0x%02x,0x%02x:0x%02x, 0x%x", + reg, index, value, ret); + if (ret < 0) + PDEBUG(D_ERR, "reg write: error %d", ret); + return ret; +} + +static int reg_read_info(struct usb_device *dev, + __u16 value) /* wValue */ +{ + int ret; + __u8 data; + + ret = usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + 0x20, /* request */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + 0, /* index */ + &data, 1, + 500); /* timeout */ + if (ret < 0) { + PDEBUG(D_ERR, "reg_read_info err %d", ret); + return 0; + } + return data; +} + +/* returns: negative is error, pos or zero is data */ +static int reg_read(struct usb_device *dev, + __u16 req, /* bRequest */ + __u16 index, /* wIndex */ + __u16 length) /* wLength (1 or 2 only) */ +{ + int ret; + __u8 buf[2]; + + buf[1] = 0; + ret = usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, + buf, length, + 500); + if (ret < 0) { + PDEBUG(D_ERR, "reg_read err %d", ret); + return -1; + } + return (buf[1] << 8) + buf[0]; +} + +static int write_vector(struct gspca_dev *gspca_dev, + __u16 data[][3]) +{ + struct usb_device *dev = gspca_dev->dev; + int ret, i = 0; + + while (data[i][0] != 0 || data[i][1] != 0 || data[i][2] != 0) { + ret = reg_write(dev, data[i][0], data[i][2], data[i][1]); + if (ret < 0) { + PDEBUG(D_ERR, + "Register write failed for 0x%x,0x%x,0x%x", + data[i][0], data[i][1], data[i][2]); + return ret; + } + i++; + } + return 0; +} + +static int spca50x_setup_qtable(struct gspca_dev *gspca_dev, + unsigned int request, + unsigned int ybase, + unsigned int cbase, + unsigned char qtable[2][64]) +{ + struct usb_device *dev = gspca_dev->dev; + int i, err; + + /* loop over y components */ + for (i = 0; i < 64; i++) { + err = reg_write(dev, request, ybase + i, qtable[0][i]); + if (err < 0) + return err; + } + + /* loop over c components */ + for (i = 0; i < 64; i++) { + err = reg_write(dev, request, cbase + i, qtable[1][i]); + if (err < 0) + return err; + } + return 0; +} + +static void spca504_acknowledged_command(struct gspca_dev *gspca_dev, + __u16 req, __u16 idx, __u16 val) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 notdone; + + reg_write(dev, req, idx, val); + notdone = reg_read(dev, 0x01, 0x0001, 1); + reg_write(dev, req, idx, val); + + PDEBUG(D_FRAM, "before wait 0x%x", notdone); + + msleep(200); + notdone = reg_read(dev, 0x01, 0x0001, 1); + PDEBUG(D_FRAM, "after wait 0x%x", notdone); +} + +static void spca504A_acknowledged_command(struct gspca_dev *gspca_dev, + __u16 req, + __u16 idx, __u16 val, __u8 stat, __u8 count) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 status; + __u8 endcode; + + reg_write(dev, req, idx, val); + status = reg_read(dev, 0x01, 0x0001, 1); + endcode = stat; + PDEBUG(D_FRAM, "Status 0x%x Need 0x%x", status, stat); + if (!count) + return; + count = 200; + while (--count > 0) { + msleep(10); + /* gsmart mini2 write a each wait setting 1 ms is enought */ +/* reg_write(dev, req, idx, val); */ + status = reg_read(dev, 0x01, 0x0001, 1); + if (status == endcode) { + PDEBUG(D_FRAM, "status 0x%x after wait 0x%x", + status, 200 - count); + break; + } + } +} + +static int spca504B_PollingDataReady(struct usb_device *dev) +{ + __u8 DataReady; + int count = 10; + + while (--count > 0) { + spca5xxRegRead(dev, 0x21, 0, &DataReady, 1); + if ((DataReady & 0x01) == 0) + break; + msleep(10); + } + return DataReady; +} + +static void spca504B_WaitCmdStatus(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 DataReady; + int count = 50; + + while (--count > 0) { + spca5xxRegRead(dev, 0x21, 1, &DataReady, 1); + + if (DataReady) { + DataReady = 0; + spca5xxRegWrite(dev, 0x21, 0, 1, &DataReady, 1); + spca5xxRegRead(dev, 0x21, 1, &DataReady, 1); + spca504B_PollingDataReady(dev); + break; + } + msleep(10); + } +} + +static void spca50x_GetFirmware(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 FW[5]; + __u8 ProductInfo[64]; + + spca5xxRegRead(dev, 0x20, 0, FW, 5); + PDEBUG(D_STREAM, "FirmWare : %d %d %d %d %d ", + FW[0], FW[1], FW[2], FW[3], FW[4]); + spca5xxRegRead(dev, 0x23, 0, ProductInfo, 64); + spca5xxRegRead(dev, 0x23, 1, ProductInfo, 64); +} + +static void spca504B_SetSizeType(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + __u8 Size; + __u8 Type; + int rc; + + Size = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode; + Type = 0; + switch (sd->bridge) { + case BRIDGE_SPCA533: + spca5xxRegWrite(dev, 0x31, 0, 0, NULL, 0); + spca504B_WaitCmdStatus(gspca_dev); + rc = spca504B_PollingDataReady(dev); + spca50x_GetFirmware(gspca_dev); + Type = 2; + spca5xxRegWrite(dev, 0x24, 0, 8, &Type, 1); + spca5xxRegRead(dev, 0x24, 8, &Type, 1); + + spca5xxRegWrite(dev, 0x25, 0, 4, &Size, 1); + spca5xxRegRead(dev, 0x25, 4, &Size, 1); + rc = spca504B_PollingDataReady(dev); + + /* Init the cam width height with some values get on init ? */ + spca5xxRegWrite(dev, 0x31, 0, 4, NULL, 0); + spca504B_WaitCmdStatus(gspca_dev); + rc = spca504B_PollingDataReady(dev); + break; + default: +/* case BRIDGE_SPCA504B: */ +/* case BRIDGE_SPCA536: */ + Type = 6; + spca5xxRegWrite(dev, 0x25, 0, 4, &Size, 1); + spca5xxRegRead(dev, 0x25, 4, &Size, 1); + spca5xxRegWrite(dev, 0x27, 0, 0, &Type, 1); + spca5xxRegRead(dev, 0x27, 0, &Type, 1); + rc = spca504B_PollingDataReady(dev); + break; + case BRIDGE_SPCA504: + Size += 3; + if (sd->subtype == AiptekMiniPenCam13) { + /* spca504a aiptek */ + spca504A_acknowledged_command(gspca_dev, + 0x08, Size, 0, + 0x80 | (Size & 0x0f), 1); + spca504A_acknowledged_command(gspca_dev, + 1, 3, 0, 0x9f, 0); + } else { + spca504_acknowledged_command(gspca_dev, 0x08, Size, 0); + } + break; + case BRIDGE_SPCA504C: + /* capture mode */ + reg_write(dev, 0xa0, (0x0500 | (Size & 0x0f)), 0x0); + reg_write(dev, 0x20, 0x01, 0x0500 | (Size & 0x0f)); + break; + } +} + +static void spca504_wait_status(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + int cnt; + + cnt = 256; + while (--cnt > 0) { + /* With this we get the status, when return 0 it's all ok */ + if (reg_read(dev, 0x06, 0x00, 1) == 0) + return; + msleep(10); + } +} + +static void spca504B_setQtable(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 Data = 3; + + spca5xxRegWrite(dev, 0x26, 0, 0, &Data, 1); + spca5xxRegRead(dev, 0x26, 0, &Data, 1); + spca504B_PollingDataReady(dev); +} + +static void sp5xx_initContBrigHueRegisters(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + int pollreg = 1; + + switch (sd->bridge) { + case BRIDGE_SPCA504: + case BRIDGE_SPCA504C: + pollreg = 0; + /* fall thru */ + default: +/* case BRIDGE_SPCA533: */ +/* case BRIDGE_SPCA504B: */ + spca5xxRegWrite(dev, 0, 0, 0x21a7, NULL, 0); /* brightness */ + spca5xxRegWrite(dev, 0, 0x20, 0x21a8, NULL, 0); /* contrast */ + spca5xxRegWrite(dev, 0, 0, 0x21ad, NULL, 0); /* hue */ + spca5xxRegWrite(dev, 0, 1, 0x21ac, NULL, 0); /* sat/hue */ + spca5xxRegWrite(dev, 0, 0x20, 0x21ae, NULL, 0); /* saturation */ + spca5xxRegWrite(dev, 0, 0, 0x21a3, NULL, 0); /* gamma */ + break; + case BRIDGE_SPCA536: + spca5xxRegWrite(dev, 0, 0, 0x20f0, NULL, 0); + spca5xxRegWrite(dev, 0, 0x21, 0x20f1, NULL, 0); + spca5xxRegWrite(dev, 0, 0x40, 0x20f5, NULL, 0); + spca5xxRegWrite(dev, 0, 1, 0x20f4, NULL, 0); + spca5xxRegWrite(dev, 0, 0x40, 0x20f6, NULL, 0); + spca5xxRegWrite(dev, 0, 0, 0x2089, NULL, 0); + break; + } + if (pollreg) + spca504B_PollingDataReady(dev); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + struct cam *cam; + __u16 vendor; + __u16 product; + __u8 fw; + + vendor = id->idVendor; + product = id->idProduct; + switch (vendor) { + case 0x041e: /* Creative cameras */ +/* switch (product) { */ +/* case 0x400b: */ +/* case 0x4012: */ +/* case 0x4013: */ +/* sd->bridge = BRIDGE_SPCA504C; */ +/* break; */ +/* } */ + break; + case 0x0458: /* Genius KYE cameras */ +/* switch (product) { */ +/* case 0x7006: */ + sd->bridge = BRIDGE_SPCA504B; +/* break; */ +/* } */ + break; + case 0x046d: /* Logitech Labtec */ + switch (product) { + case 0x0905: + sd->subtype = LogitechClickSmart820; + sd->bridge = BRIDGE_SPCA533; + break; + case 0x0960: + sd->subtype = LogitechClickSmart420; + sd->bridge = BRIDGE_SPCA504C; + break; + } + break; + case 0x0471: /* Philips */ +/* switch (product) { */ +/* case 0x0322: */ + sd->bridge = BRIDGE_SPCA504B; +/* break; */ +/* } */ + break; + case 0x04a5: /* Benq */ + switch (product) { + case 0x3003: + sd->bridge = BRIDGE_SPCA504B; + break; + case 0x3008: + case 0x300a: + sd->bridge = BRIDGE_SPCA533; + break; + } + break; + case 0x04f1: /* JVC */ +/* switch (product) { */ +/* case 0x1001: */ + sd->bridge = BRIDGE_SPCA504B; +/* break; */ +/* } */ + break; + case 0x04fc: /* SunPlus */ + switch (product) { + case 0x500c: + sd->bridge = BRIDGE_SPCA504B; + break; + case 0x504a: +/* try to get the firmware as some cam answer 2.0.1.2.2 + * and should be a spca504b then overwrite that setting */ + spca5xxRegRead(dev, 0x20, 0, &fw, 1); + if (fw == 1) { + sd->subtype = AiptekMiniPenCam13; + sd->bridge = BRIDGE_SPCA504; + } else if (fw == 2) { + sd->bridge = BRIDGE_SPCA504B; + } else + return -ENODEV; + break; + case 0x504b: + sd->bridge = BRIDGE_SPCA504B; + break; + case 0x5330: + sd->bridge = BRIDGE_SPCA533; + break; + case 0x5360: + sd->bridge = BRIDGE_SPCA536; + break; + case 0xffff: + sd->bridge = BRIDGE_SPCA504B; + break; + } + break; + case 0x052b: /* ?? Megapix */ +/* switch (product) { */ +/* case 0x1513: */ + sd->subtype = MegapixV4; + sd->bridge = BRIDGE_SPCA533; +/* break; */ +/* } */ + break; + case 0x0546: /* Polaroid */ + switch (product) { + case 0x3155: + sd->bridge = BRIDGE_SPCA533; + break; + case 0x3191: + case 0x3273: + sd->bridge = BRIDGE_SPCA504B; + break; + } + break; + case 0x055f: /* Mustek cameras */ + switch (product) { + case 0xc211: + sd->bridge = BRIDGE_SPCA536; + break; + case 0xc230: + case 0xc232: + sd->bridge = BRIDGE_SPCA533; + break; + case 0xc360: + sd->bridge = BRIDGE_SPCA536; + break; + case 0xc420: + sd->bridge = BRIDGE_SPCA504; + break; + case 0xc430: + case 0xc440: + sd->bridge = BRIDGE_SPCA533; + break; + case 0xc520: + sd->bridge = BRIDGE_SPCA504; + break; + case 0xc530: + case 0xc540: + case 0xc630: + case 0xc650: + sd->bridge = BRIDGE_SPCA533; + break; + } + break; + case 0x05da: /* Digital Dream cameras */ +/* switch (product) { */ +/* case 0x1018: */ + sd->bridge = BRIDGE_SPCA504B; +/* break; */ +/* } */ + break; + case 0x06d6: /* Trust */ +/* switch (product) { */ +/* case 0x0031: */ + sd->bridge = BRIDGE_SPCA533; /* SPCA533A */ +/* break; */ +/* } */ + break; + case 0x0733: /* Rebadged ViewQuest (Intel) and ViewQuest cameras */ + switch (product) { + case 0x1311: + case 0x1314: + case 0x2211: + case 0x2221: + sd->bridge = BRIDGE_SPCA533; + break; + case 0x3261: + case 0x3281: + sd->bridge = BRIDGE_SPCA536; + break; + } + break; + case 0x08ca: /* Aiptek */ + switch (product) { + case 0x0104: + case 0x0106: + sd->bridge = BRIDGE_SPCA533; + break; + case 0x2008: + sd->bridge = BRIDGE_SPCA504B; + break; + case 0x2010: + sd->bridge = BRIDGE_SPCA533; + break; + case 0x2016: + case 0x2018: + sd->bridge = BRIDGE_SPCA504B; + break; + case 0x2020: + case 0x2022: + sd->bridge = BRIDGE_SPCA533; + break; + case 0x2024: + sd->bridge = BRIDGE_SPCA536; + break; + case 0x2028: + sd->bridge = BRIDGE_SPCA533; + break; + case 0x2040: + case 0x2042: + case 0x2060: + sd->bridge = BRIDGE_SPCA536; + break; + } + break; + case 0x0d64: /* SunPlus */ +/* switch (product) { */ +/* case 0x0303: */ + sd->bridge = BRIDGE_SPCA536; +/* break; */ +/* } */ + break; + } + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + + switch (sd->bridge) { + default: +/* case BRIDGE_SPCA504B: */ +/* case BRIDGE_SPCA504: */ +/* case BRIDGE_SPCA536: */ + cam->cam_mode = vga_mode; + cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + break; + case BRIDGE_SPCA533: + cam->cam_mode = custom_mode; + cam->nmodes = sizeof custom_mode / sizeof custom_mode[0]; + break; + case BRIDGE_SPCA504C: + cam->cam_mode = vga_mode2; + cam->nmodes = sizeof vga_mode2 / sizeof vga_mode2[0]; + break; + } + sd->qindex = 5; /* set the quantization table */ + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; + sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value; + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + int rc; + __u8 Data; + __u8 i; + __u8 info[6]; + int err_code; + + switch (sd->bridge) { + case BRIDGE_SPCA504B: + spca5xxRegWrite(dev, 0x1d, 0, 0, NULL, 0); + spca5xxRegWrite(dev, 0, 1, 0x2306, NULL, 0); + spca5xxRegWrite(dev, 0, 0, 0x0d04, NULL, 0); + spca5xxRegWrite(dev, 0, 0, 0x2000, NULL, 0); + spca5xxRegWrite(dev, 0, 0x13, 0x2301, NULL, 0); + spca5xxRegWrite(dev, 0, 0, 0x2306, NULL, 0); + /* fall thru */ + case BRIDGE_SPCA533: + rc = spca504B_PollingDataReady(dev); + spca50x_GetFirmware(gspca_dev); + break; + case BRIDGE_SPCA536: + spca50x_GetFirmware(gspca_dev); + spca5xxRegRead(dev, 0x00, 0x5002, &Data, 1); + Data = 0; + spca5xxRegWrite(dev, 0x24, 0, 0, &Data, 1); + spca5xxRegRead(dev, 0x24, 0, &Data, 1); + rc = spca504B_PollingDataReady(dev); + spca5xxRegWrite(dev, 0x34, 0, 0, NULL, 0); + spca504B_WaitCmdStatus(gspca_dev); + break; + case BRIDGE_SPCA504C: /* pccam600 */ + PDEBUG(D_STREAM, "Opening SPCA504 (PC-CAM 600)"); + reg_write(dev, 0xe0, 0x0000, 0x0000); + reg_write(dev, 0xe0, 0x0000, 0x0001); /* reset */ + spca504_wait_status(gspca_dev); + if (sd->subtype == LogitechClickSmart420) + write_vector(gspca_dev, + spca504A_clicksmart420_open_data); + else + write_vector(gspca_dev, spca504_pccam600_open_data); + err_code = spca50x_setup_qtable(gspca_dev, + 0x00, 0x2800, + 0x2840, qtable_creative_pccam); + if (err_code < 0) { + PDEBUG(D_ERR|D_STREAM, "spca50x_setup_qtable failed"); + return err_code; + } + break; + default: +/* case BRIDGE_SPCA504: */ + PDEBUG(D_STREAM, "Opening SPCA504"); + if (sd->subtype == AiptekMiniPenCam13) { + /*****************************/ + for (i = 0; i < 6; i++) + info[i] = reg_read_info(dev, i); + PDEBUG(D_STREAM, + "Read info: %d %d %d %d %d %d." + " Should be 1,0,2,2,0,0", + info[0], info[1], info[2], + info[3], info[4], info[5]); + /* spca504a aiptek */ + /* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */ + spca504A_acknowledged_command(gspca_dev, 0x24, + 8, 3, 0x9e, 1); + /* Twice sequencial need status 0xff->0x9e->0x9d */ + spca504A_acknowledged_command(gspca_dev, 0x24, + 8, 3, 0x9e, 0); + + spca504A_acknowledged_command(gspca_dev, 0x24, + 0, 0, 0x9d, 1); + /******************************/ + /* spca504a aiptek */ + spca504A_acknowledged_command(gspca_dev, 0x08, + 6, 0, 0x86, 1); +/* reg_write (dev, 0, 0x2000, 0); */ +/* reg_write (dev, 0, 0x2883, 1); */ +/* spca504A_acknowledged_command (gspca_dev, 0x08, + 6, 0, 0x86, 1); */ +/* spca504A_acknowledged_command (gspca_dev, 0x24, + 0, 0, 0x9D, 1); */ + reg_write(dev, 0x0, 0x270c, 0x5); /* L92 sno1t.txt */ + reg_write(dev, 0x0, 0x2310, 0x5); + spca504A_acknowledged_command(gspca_dev, 0x01, + 0x0f, 0, 0xff, 0); + } + /* setup qtable */ + reg_write(dev, 0, 0x2000, 0); + reg_write(dev, 0, 0x2883, 1); + err_code = spca50x_setup_qtable(gspca_dev, + 0x00, 0x2800, + 0x2840, + qtable_spca504_default); + if (err_code < 0) { + PDEBUG(D_ERR, "spca50x_setup_qtable failed"); + return err_code; + } + break; + } + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + int rc; + int enable; + __u8 i; + __u8 info[6]; + + if (sd->bridge == BRIDGE_SPCA504B) + spca504B_setQtable(gspca_dev); + spca504B_SetSizeType(gspca_dev); + switch (sd->bridge) { + default: +/* case BRIDGE_SPCA504B: */ +/* case BRIDGE_SPCA533: */ +/* case BRIDGE_SPCA536: */ + if (sd->subtype == MegapixV4 || + sd->subtype == LogitechClickSmart820) { + spca5xxRegWrite(dev, 0xf0, 0, 0, NULL, 0); + spca504B_WaitCmdStatus(gspca_dev); + spca5xxRegRead(dev, 0xf0, 4, NULL, 0); + spca504B_WaitCmdStatus(gspca_dev); + } else { + spca5xxRegWrite(dev, 0x31, 0, 4, NULL, 0); + spca504B_WaitCmdStatus(gspca_dev); + rc = spca504B_PollingDataReady(dev); + } + break; + case BRIDGE_SPCA504: + if (sd->subtype == AiptekMiniPenCam13) { + for (i = 0; i < 6; i++) + info[i] = reg_read_info(dev, i); + PDEBUG(D_STREAM, + "Read info: %d %d %d %d %d %d." + " Should be 1,0,2,2,0,0", + info[0], info[1], info[2], + info[3], info[4], info[5]); + /* spca504a aiptek */ + /* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */ + spca504A_acknowledged_command(gspca_dev, 0x24, + 8, 3, 0x9e, 1); + /* Twice sequencial need status 0xff->0x9e->0x9d */ + spca504A_acknowledged_command(gspca_dev, 0x24, + 8, 3, 0x9e, 0); + spca504A_acknowledged_command(gspca_dev, 0x24, + 0, 0, 0x9d, 1); + } else { + spca504_acknowledged_command(gspca_dev, 0x24, 8, 3); + for (i = 0; i < 6; i++) + info[i] = reg_read_info(dev, i); + PDEBUG(D_STREAM, + "Read info: %d %d %d %d %d %d." + " Should be 1,0,2,2,0,0", + info[0], info[1], info[2], + info[3], info[4], info[5]); + spca504_acknowledged_command(gspca_dev, 0x24, 8, 3); + spca504_acknowledged_command(gspca_dev, 0x24, 0, 0); + } + spca504B_SetSizeType(gspca_dev); + reg_write(dev, 0x0, 0x270c, 0x5); /* L92 sno1t.txt */ + reg_write(dev, 0x0, 0x2310, 0x5); + break; + case BRIDGE_SPCA504C: + if (sd->subtype == LogitechClickSmart420) { + write_vector(gspca_dev, + spca504A_clicksmart420_init_data); + } else { + write_vector(gspca_dev, spca504_pccam600_init_data); + } + enable = (sd->autogain ? 0x4 : 0x1); + reg_write(dev, 0x0c, 0x0000, enable); /* auto exposure */ + reg_write(dev, 0xb0, 0x0000, enable); /* auto whiteness */ + + /* set default exposure compensation and whiteness balance */ + reg_write(dev, 0x30, 0x0001, 800); /* ~ 20 fps */ + reg_write(dev, 0x30, 0x0002, 1600); + spca504B_SetSizeType(gspca_dev); + break; + } + sp5xx_initContBrigHueRegisters(gspca_dev); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + + switch (sd->bridge) { + default: +/* case BRIDGE_SPCA533: */ +/* case BRIDGE_SPCA536: */ +/* case BRIDGE_SPCA504B: */ + spca5xxRegWrite(dev, 0x31, 0, 0, NULL, 0); + spca504B_WaitCmdStatus(gspca_dev); + spca504B_PollingDataReady(dev); + break; + case BRIDGE_SPCA504: + case BRIDGE_SPCA504C: + reg_write(dev, 0x00, 0x2000, 0x0000); + + if (sd->subtype == AiptekMiniPenCam13) { + /* spca504a aiptek */ +/* spca504A_acknowledged_command(gspca_dev, 0x08, + 6, 0, 0x86, 1); */ + spca504A_acknowledged_command(gspca_dev, 0x24, + 0x00, 0x00, 0x9d, 1); + spca504A_acknowledged_command(gspca_dev, 0x01, + 0x0f, 0x00, 0xff, 1); + } else { + spca504_acknowledged_command(gspca_dev, 0x24, 0, 0); + reg_write(dev, 0x01, 0x000f, 0x0); + } + break; + } +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + unsigned char *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, sof = 0; + unsigned char *s, *d; + static unsigned char ffd9[] = {0xff, 0xd9}; + +/* frames are jpeg 4.1.1 without 0xff escape */ + switch (sd->bridge) { + case BRIDGE_SPCA533: + if (data[0] == 0xff) { + if (data[1] != 0x01) { /* drop packet */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + } + sof = 1; + data += SPCA533_OFFSET_DATA; + len -= SPCA533_OFFSET_DATA; + } else { + data += 1; + len -= 1; + } + break; + case BRIDGE_SPCA536: + if (data[0] == 0xff) { + sof = 1; + data += SPCA536_OFFSET_DATA; + len -= SPCA536_OFFSET_DATA; + } else { + data += 2; + len -= 2; + } + break; + default: +/* case BRIDGE_SPCA504: */ +/* case BRIDGE_SPCA504B: */ + switch (data[0]) { + case 0xfe: /* start of frame */ + sof = 1; + data += SPCA50X_OFFSET_DATA; + len -= SPCA50X_OFFSET_DATA; + break; + case 0xff: /* drop packet */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + default: + data += 1; + len -= 1; + break; + } + break; + case BRIDGE_SPCA504C: + switch (data[0]) { + case 0xfe: /* start of frame */ + sof = 1; + data += SPCA504_PCCAM600_OFFSET_DATA; + len -= SPCA504_PCCAM600_OFFSET_DATA; + break; + case 0xff: /* drop packet */ +/* gspca_dev->last_packet_type = DISCARD_PACKET; */ + return; + default: + data += 1; + len -= 1; + break; + } + break; + } + if (sof) { /* start of frame */ + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + ffd9, 2); + + /* put the JPEG header in the new frame */ + jpeg_put_header(gspca_dev, frame, + ((struct sd *) gspca_dev)->qindex, + 0x22); + } + + /* add 0x00 after 0xff */ + for (i = len; --i >= 0; ) + if (data[i] == 0xff) + break; + if (i < 0) { /* no 0xff */ + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + return; + } + s = data; + d = sd->packet; + for (i = 0; i < len; i++) { + *d++ = *s++; + if (s[-1] == 0xff) + *d++ = 0x00; + } + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + sd->packet, d - sd->packet); +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + + switch (sd->bridge) { + default: +/* case BRIDGE_SPCA533: */ +/* case BRIDGE_SPCA504B: */ +/* case BRIDGE_SPCA504: */ +/* case BRIDGE_SPCA504C: */ + reg_write(dev, 0x0, 0x21a7, sd->brightness); + break; + case BRIDGE_SPCA536: + reg_write(dev, 0x0, 0x20f0, sd->brightness); + break; + } +} + +static void getbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + __u16 brightness = 0; + + switch (sd->bridge) { + default: +/* case BRIDGE_SPCA533: */ +/* case BRIDGE_SPCA504B: */ +/* case BRIDGE_SPCA504: */ +/* case BRIDGE_SPCA504C: */ + brightness = reg_read(dev, 0x0, 0x21a7, 2); + break; + case BRIDGE_SPCA536: + brightness = reg_read(dev, 0x0, 0x20f0, 2); + break; + } + sd->brightness = ((brightness & 0xff) - 128) % 255; +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + + switch (sd->bridge) { + default: +/* case BRIDGE_SPCA533: */ +/* case BRIDGE_SPCA504B: */ +/* case BRIDGE_SPCA504: */ +/* case BRIDGE_SPCA504C: */ + reg_write(dev, 0x0, 0x21a8, sd->contrast); + break; + case BRIDGE_SPCA536: + reg_write(dev, 0x0, 0x20f1, sd->contrast); + break; + } +} + +static void getcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + + switch (sd->bridge) { + default: +/* case BRIDGE_SPCA533: */ +/* case BRIDGE_SPCA504B: */ +/* case BRIDGE_SPCA504: */ +/* case BRIDGE_SPCA504C: */ + sd->contrast = reg_read(dev, 0x0, 0x21a8, 2); + break; + case BRIDGE_SPCA536: + sd->contrast = reg_read(dev, 0x0, 0x20f1, 2); + break; + } +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + + switch (sd->bridge) { + default: +/* case BRIDGE_SPCA533: */ +/* case BRIDGE_SPCA504B: */ +/* case BRIDGE_SPCA504: */ +/* case BRIDGE_SPCA504C: */ + reg_write(dev, 0x0, 0x21ae, sd->colors); + break; + case BRIDGE_SPCA536: + reg_write(dev, 0x0, 0x20f6, sd->colors); + break; + } +} + +static void getcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + + switch (sd->bridge) { + default: +/* case BRIDGE_SPCA533: */ +/* case BRIDGE_SPCA504B: */ +/* case BRIDGE_SPCA504: */ +/* case BRIDGE_SPCA504C: */ + sd->colors = reg_read(dev, 0x0, 0x21ae, 2) >> 1; + break; + case BRIDGE_SPCA536: + sd->colors = reg_read(dev, 0x0, 0x20f6, 2) >> 1; + break; + } +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getbrightness(gspca_dev); + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcontrast(gspca_dev); + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + getcolors(gspca_dev); + *val = sd->colors; + return 0; +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + return 0; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x041e, 0x400b), DVNM("Creative PC-CAM 600")}, + {USB_DEVICE(0x041e, 0x4012), DVNM("PC-Cam350")}, + {USB_DEVICE(0x041e, 0x4013), DVNM("Creative Pccam750")}, + {USB_DEVICE(0x0458, 0x7006), DVNM("Genius Dsc 1.3 Smart")}, + {USB_DEVICE(0x046d, 0x0905), DVNM("Logitech ClickSmart 820")}, + {USB_DEVICE(0x046d, 0x0960), DVNM("Logitech ClickSmart 420")}, + {USB_DEVICE(0x0471, 0x0322), DVNM("Philips DMVC1300K")}, + {USB_DEVICE(0x04a5, 0x3003), DVNM("Benq DC 1300")}, + {USB_DEVICE(0x04a5, 0x3008), DVNM("Benq DC 1500")}, + {USB_DEVICE(0x04a5, 0x300a), DVNM("Benq DC3410")}, + {USB_DEVICE(0x04f1, 0x1001), DVNM("JVC GC A50")}, + {USB_DEVICE(0x04fc, 0x500c), DVNM("Sunplus CA500C")}, + {USB_DEVICE(0x04fc, 0x504a), DVNM("Aiptek Mini PenCam 1.3")}, + {USB_DEVICE(0x04fc, 0x504b), DVNM("Maxell MaxPocket LE 1.3")}, + {USB_DEVICE(0x04fc, 0x5330), DVNM("Digitrex 2110")}, + {USB_DEVICE(0x04fc, 0x5360), DVNM("Sunplus Generic")}, + {USB_DEVICE(0x04fc, 0xffff), DVNM("Pure DigitalDakota")}, + {USB_DEVICE(0x052b, 0x1513), DVNM("Megapix V4")}, + {USB_DEVICE(0x0546, 0x3155), DVNM("Polaroid PDC3070")}, + {USB_DEVICE(0x0546, 0x3191), DVNM("Polaroid Ion 80")}, + {USB_DEVICE(0x0546, 0x3273), DVNM("Polaroid PDC2030")}, + {USB_DEVICE(0x055f, 0xc211), DVNM("Kowa Bs888e Microcamera")}, + {USB_DEVICE(0x055f, 0xc230), DVNM("Mustek Digicam 330K")}, + {USB_DEVICE(0x055f, 0xc232), DVNM("Mustek MDC3500")}, + {USB_DEVICE(0x055f, 0xc360), DVNM("Mustek DV4000 Mpeg4 ")}, + {USB_DEVICE(0x055f, 0xc420), DVNM("Mustek gSmart Mini 2")}, + {USB_DEVICE(0x055f, 0xc430), DVNM("Mustek Gsmart LCD 2")}, + {USB_DEVICE(0x055f, 0xc440), DVNM("Mustek DV 3000")}, + {USB_DEVICE(0x055f, 0xc520), DVNM("Mustek gSmart Mini 3")}, + {USB_DEVICE(0x055f, 0xc530), DVNM("Mustek Gsmart LCD 3")}, + {USB_DEVICE(0x055f, 0xc540), DVNM("Gsmart D30")}, + {USB_DEVICE(0x055f, 0xc630), DVNM("Mustek MDC4000")}, + {USB_DEVICE(0x055f, 0xc650), DVNM("Mustek MDC5500Z")}, + {USB_DEVICE(0x05da, 0x1018), DVNM("Digital Dream Enigma 1.3")}, + {USB_DEVICE(0x06d6, 0x0031), DVNM("Trust 610 LCD PowerC@m Zoom")}, + {USB_DEVICE(0x0733, 0x1311), DVNM("Digital Dream Epsilon 1.3")}, + {USB_DEVICE(0x0733, 0x1314), DVNM("Mercury 2.1MEG Deluxe Classic Cam")}, + {USB_DEVICE(0x0733, 0x2211), DVNM("Jenoptik jdc 21 LCD")}, + {USB_DEVICE(0x0733, 0x2221), DVNM("Mercury Digital Pro 3.1p")}, + {USB_DEVICE(0x0733, 0x3261), DVNM("Concord 3045 spca536a")}, + {USB_DEVICE(0x0733, 0x3281), DVNM("Cyberpix S550V")}, + {USB_DEVICE(0x08ca, 0x0104), DVNM("Aiptek PocketDVII 1.3")}, + {USB_DEVICE(0x08ca, 0x0106), DVNM("Aiptek Pocket DV3100+")}, + {USB_DEVICE(0x08ca, 0x2008), DVNM("Aiptek Mini PenCam 2 M")}, + {USB_DEVICE(0x08ca, 0x2010), DVNM("Aiptek PocketCam 3M")}, + {USB_DEVICE(0x08ca, 0x2016), DVNM("Aiptek PocketCam 2 Mega")}, + {USB_DEVICE(0x08ca, 0x2018), DVNM("Aiptek Pencam SD 2M")}, + {USB_DEVICE(0x08ca, 0x2020), DVNM("Aiptek Slim 3000F")}, + {USB_DEVICE(0x08ca, 0x2022), DVNM("Aiptek Slim 3200")}, + {USB_DEVICE(0x08ca, 0x2024), DVNM("Aiptek DV3500 Mpeg4 ")}, + {USB_DEVICE(0x08ca, 0x2028), DVNM("Aiptek PocketCam4M")}, + {USB_DEVICE(0x08ca, 0x2040), DVNM("Aiptek PocketDV4100M")}, + {USB_DEVICE(0x08ca, 0x2042), DVNM("Aiptek PocketDV5100")}, + {USB_DEVICE(0x08ca, 0x2060), DVNM("Aiptek PocketDV5300")}, + {USB_DEVICE(0x0d64, 0x0303), DVNM("Sunplus FashionCam DXG")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c new file mode 100644 index 000000000000..c22b301ebae0 --- /dev/null +++ b/drivers/media/video/gspca/t613.c @@ -0,0 +1,1013 @@ +/* + *Notes: * t613 + tas5130A + * * Focus to light do not balance well as in win. + * Quality in win is not good, but its kinda better. + * * Fix some "extraneous bytes", most of apps will show the image anyway + * * Gamma table, is there, but its really doing something? + * * 7~8 Fps, its ok, max on win its 10. + * Costantino Leandro + * + * V4L2 by Jean-Francois Moine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "t613" +#include "gspca.h" +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +struct control_menu_info { + int value; + char name[32]; +}; + +#define MAX_GAMMA 0x10 /* 0 to 15 */ + +/* From LUVCVIEW */ +#define V4L2_CID_EFFECTS (V4L2_CID_PRIVATE_BASE + 3) + +MODULE_AUTHOR("Leandro Costantino "); +MODULE_DESCRIPTION("GSPCA/T613 (JPEG Compliance) USB Camera Driver"); +MODULE_LICENSE("GPL"); + +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + unsigned char brightness; + unsigned char contrast; + unsigned char colors; + unsigned char autogain; + unsigned char gamma; + unsigned char sharpness; + unsigned char freq; + unsigned char whitebalance; + unsigned char mirror; + unsigned char effect; +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setlowlight(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getlowlight(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setflip(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getflip(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_seteffect(struct gspca_dev *gspca_dev, __s32 val); +static int sd_geteffect(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 0x0f, + .step = 1, + .default_value = 0x09, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 0x0d, + .step = 1, + .default_value = 0x07, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define SD_COLOR 2 + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Color", + .minimum = 0, + .maximum = 0x0f, + .step = 1, + .default_value = 0x05, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, +#define SD_GAMMA 3 + { + { + .id = V4L2_CID_GAMMA, /* (gamma on win) */ + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gamma (Untested)", + .minimum = 0, + .maximum = MAX_GAMMA, + .step = 1, + .default_value = 0x09, + }, + .set = sd_setgamma, + .get = sd_getgamma, + }, +#define SD_AUTOGAIN 4 + { + { + .id = V4L2_CID_GAIN, /* here, i activate only the lowlight, + * some apps dont bring up the + * backligth_compensation control) */ + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Low Light", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0x01, + }, + .set = sd_setlowlight, + .get = sd_getlowlight, + }, +#define SD_MIRROR 5 + { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mirror Image", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .set = sd_setflip, + .get = sd_getflip + }, +#define SD_LIGHTFREQ 6 + { + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light Frequency Filter", + .minimum = 1, /* 1 -> 0x50, 2->0x60 */ + .maximum = 2, + .step = 1, + .default_value = 1, + }, + .set = sd_setfreq, + .get = sd_getfreq}, + +#define SD_WHITE_BALANCE 7 + { + { + .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "White Balance", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, + .set = sd_setwhitebalance, + .get = sd_getwhitebalance + }, +#define SD_SHARPNESS 8 /* (aka definition on win) */ + { + { + .id = V4L2_CID_SHARPNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Sharpness", + .minimum = 0, + .maximum = MAX_GAMMA, /* 0 to 16 */ + .step = 1, + .default_value = 0x06, + }, + .set = sd_setsharpness, + .get = sd_getsharpness, + }, +#define SD_EFFECTS 9 + { + { + .id = V4L2_CID_EFFECTS, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Webcam Effects", + .minimum = 0, + .maximum = 4, + .step = 1, + .default_value = 0, + }, + .set = sd_seteffect, + .get = sd_geteffect + }, +}; + +static struct control_menu_info effects_control[] = { + {0, "Normal"}, + {1, "Emboss"}, /* disabled */ + {2, "Monochrome"}, + {3, "Sepia"}, + {4, "Sketch"}, + {5, "Sun Effect"}, /* disabled */ + {6, "Negative"}, +}; + +#define NUM_EFFECTS_CONTROL \ + (sizeof(effects_control)/sizeof(effects_control[0])) + +static struct cam_mode vga_mode_t16[] = { + {V4L2_PIX_FMT_JPEG, 160, 120, 4}, + {V4L2_PIX_FMT_JPEG, 176, 144, 3}, + {V4L2_PIX_FMT_JPEG, 320, 240, 2}, + {V4L2_PIX_FMT_JPEG, 352, 288, 1}, + {V4L2_PIX_FMT_JPEG, 640, 480, 0}, +}; + +#define T16_OFFSET_DATA 631 +#define MAX_EFFECTS 7 +/* easily done by soft, this table could be removed, + * i keep it here just in case */ +unsigned char effects_table[MAX_EFFECTS][6] = { + {0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x00}, /* Normal */ + {0xa8, 0xc8, 0xc6, 0x52, 0xc0, 0x04}, /* Repujar */ + {0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x20}, /* Monochrome */ + {0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x80}, /* Sepia */ + {0xa8, 0xc8, 0xc6, 0x52, 0xc0, 0x02}, /* Croquis */ + {0xa8, 0xc8, 0xc6, 0xd2, 0xc0, 0x10}, /* Sun Effect */ + {0xa8, 0xc8, 0xc6, 0xd2, 0xc0, 0x40}, /* Negative */ +}; + +unsigned char gamma_table[MAX_GAMMA][34] = { + {0x90, 0x00, 0x91, 0x3e, 0x92, 0x69, 0x93, 0x85, + 0x94, 0x95, 0x95, 0xa1, 0x96, 0xae, 0x97, 0xb9, + 0x98, 0xc2, 0x99, 0xcb, 0x9a, 0xd4, 0x9b, 0xdb, + 0x9c, 0xe3, 0x9d, 0xea, 0x9e, 0xf1, 0x9f, 0xf8, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x33, 0x92, 0x5A, 0x93, 0x75, + 0x94, 0x85, 0x95, 0x93, 0x96, 0xA1, 0x97, 0xAD, + 0x98, 0xB7, 0x99, 0xC2, 0x9A, 0xCB, 0x9B, 0xD4, + 0x9C, 0xDE, 0x9D, 0xE7, 0x9E, 0xF0, 0x9F, 0xF7, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x2F, 0x92, 0x51, 0x93, 0x6B, + 0x94, 0x7C, 0x95, 0x8A, 0x96, 0x99, 0x97, 0xA6, + 0x98, 0xB1, 0x99, 0xBC, 0x9A, 0xC6, 0x9B, 0xD0, + 0x9C, 0xDB, 0x9D, 0xE4, 0x9E, 0xED, 0x9F, 0xF6, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x29, 0x92, 0x48, 0x93, 0x60, + 0x94, 0x72, 0x95, 0x81, 0x96, 0x90, 0x97, 0x9E, + 0x98, 0xAA, 0x99, 0xB5, 0x9A, 0xBF, 0x9B, 0xCB, + 0x9C, 0xD6, 0x9D, 0xE1, 0x9E, 0xEB, 0x9F, 0xF5, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x23, 0x92, 0x3F, 0x93, 0x55, + 0x94, 0x68, 0x95, 0x77, 0x96, 0x86, 0x97, 0x95, + 0x98, 0xA2, 0x99, 0xAD, 0x9A, 0xB9, 0x9B, 0xC6, + 0x9C, 0xD2, 0x9D, 0xDE, 0x9E, 0xE9, 0x9F, 0xF4, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x1B, 0x92, 0x33, 0x93, 0x48, + 0x94, 0x59, 0x95, 0x69, 0x96, 0x79, 0x97, 0x87, + 0x98, 0x96, 0x99, 0xA3, 0x9A, 0xB1, 0x9B, 0xBE, + 0x9C, 0xCC, 0x9D, 0xDA, 0x9E, 0xE7, 0x9F, 0xF3, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x02, 0x92, 0x10, 0x93, 0x20, + 0x94, 0x32, 0x95, 0x40, 0x96, 0x57, 0x97, 0x67, + 0x98, 0x77, 0x99, 0x88, 0x9a, 0x99, 0x9b, 0xaa, + 0x9c, 0xbb, 0x9d, 0xcc, 0x9e, 0xdd, 0x9f, 0xee, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x02, 0x92, 0x14, 0x93, 0x26, + 0x94, 0x38, 0x95, 0x4A, 0x96, 0x60, 0x97, 0x70, + 0x98, 0x80, 0x99, 0x90, 0x9A, 0xA0, 0x9B, 0xB0, + 0x9C, 0xC0, 0x9D, 0xD0, 0x9E, 0xE0, 0x9F, 0xF0, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x10, 0x92, 0x22, 0x93, 0x35, + 0x94, 0x47, 0x95, 0x5A, 0x96, 0x69, 0x97, 0x79, + 0x98, 0x88, 0x99, 0x97, 0x9A, 0xA7, 0x9B, 0xB6, + 0x9C, 0xC4, 0x9D, 0xD3, 0x9E, 0xE0, 0x9F, 0xF0, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x10, 0x92, 0x26, 0x93, 0x40, + 0x94, 0x54, 0x95, 0x65, 0x96, 0x75, 0x97, 0x84, + 0x98, 0x93, 0x99, 0xa1, 0x9a, 0xb0, 0x9b, 0xbd, + 0x9c, 0xca, 0x9d, 0xd6, 0x9e, 0xe0, 0x9f, 0xf0, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x18, 0x92, 0x2B, 0x93, 0x44, + 0x94, 0x60, 0x95, 0x70, 0x96, 0x80, 0x97, 0x8E, + 0x98, 0x9C, 0x99, 0xAA, 0x9A, 0xB7, 0x9B, 0xC4, + 0x9C, 0xD0, 0x9D, 0xD8, 0x9E, 0xE2, 0x9F, 0xF0, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x1A, 0x92, 0x34, 0x93, 0x52, + 0x94, 0x66, 0x95, 0x7E, 0x96, 0x8D, 0x97, 0x9B, + 0x98, 0xA8, 0x99, 0xB4, 0x9A, 0xC0, 0x9B, 0xCB, + 0x9C, 0xD6, 0x9D, 0xE1, 0x9E, 0xEB, 0x9F, 0xF5, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x3F, 0x92, 0x5A, 0x93, 0x6E, + 0x94, 0x7F, 0x95, 0x8E, 0x96, 0x9C, 0x97, 0xA8, + 0x98, 0xB4, 0x99, 0xBF, 0x9A, 0xC9, 0x9B, 0xD3, + 0x9C, 0xDC, 0x9D, 0xE5, 0x9E, 0xEE, 0x9F, 0xF6, + 0xA0, 0xFF}, + {0x90, 0x00, 0x91, 0x54, 0x92, 0x6F, 0x93, 0x83, + 0x94, 0x93, 0x95, 0xA0, 0x96, 0xAD, 0x97, 0xB7, + 0x98, 0xC2, 0x99, 0xCB, 0x9A, 0xD4, 0x9B, 0xDC, + 0x9C, 0xE4, 0x9D, 0xEB, 0x9E, 0xF2, 0x9F, 0xF9, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x6E, 0x92, 0x88, 0x93, 0x9A, + 0x94, 0xA8, 0x95, 0xB3, 0x96, 0xBD, 0x97, 0xC6, + 0x98, 0xCF, 0x99, 0xD6, 0x9A, 0xDD, 0x9B, 0xE3, + 0x9C, 0xE9, 0x9D, 0xEF, 0x9E, 0xF4, 0x9F, 0xFA, + 0xa0, 0xff}, + {0x90, 0x00, 0x91, 0x93, 0x92, 0xA8, 0x93, 0xB7, + 0x94, 0xC1, 0x95, 0xCA, 0x96, 0xD2, 0x97, 0xD8, + 0x98, 0xDE, 0x99, 0xE3, 0x9A, 0xE8, 0x9B, 0xED, + 0x9C, 0xF1, 0x9D, 0xF5, 0x9E, 0xF8, 0x9F, 0xFC, + 0xA0, 0xFF} +}; + +static __u8 tas5130a_sensor_init[][8] = { + {0x62, 0x08, 0x63, 0x70, 0x64, 0x1d, 0x60, 0x09}, + {0x62, 0x20, 0x63, 0x01, 0x64, 0x02, 0x60, 0x09}, + {0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09}, + {0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09}, + {}, +}; + +static void t16RegRead(struct usb_device *dev, + __u16 index, __u8 *buffer, __u16 length) +{ + usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + 0, /* request */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, buffer, length, 500); +} + +static void t16RegWrite(struct usb_device *dev, + __u16 value, + __u16 index, __u8 *buffer, __u16 length) +{ + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + 0, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, buffer, length, 500); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x01; + + cam->cam_mode = vga_mode_t16; + cam->nmodes = ARRAY_SIZE(vga_mode_t16); + + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; + sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value; + sd->gamma = sd_ctrls[SD_GAMMA].qctrl.default_value; + sd->mirror = sd_ctrls[SD_MIRROR].qctrl.default_value; + sd->freq = sd_ctrls[SD_LIGHTFREQ].qctrl.default_value; + sd->whitebalance = sd_ctrls[SD_WHITE_BALANCE].qctrl.default_value; + sd->sharpness = sd_ctrls[SD_SHARPNESS].qctrl.default_value; + sd->effect = sd_ctrls[SD_EFFECTS].qctrl.default_value; + return 0; +} + +static int init_default_parameters(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + + /* some of this registers are not really neded, because + * they are overriden by setbrigthness, setcontrast, etc, + * but wont hurt anyway, and can help someone with similar webcam + * to see the initial parameters.*/ + int i = 0; + __u8 test_byte; + + static unsigned char read_indexs[] = + { 0x06, 0x07, 0x0a, 0x0b, 0x66, 0x80, 0x81, 0x8e, 0x8f, 0xa5, + 0xa6, 0xa8, 0xbb, 0xbc, 0xc6, 0x00, 0x00 }; + static unsigned char n1[6] = + {0x08, 0x03, 0x09, 0x03, 0x12, 0x04}; + static unsigned char n2[2] = + {0x08, 0x00}; + static unsigned char nset[6] = + { 0x61, 0x68, 0x62, 0xff, 0x60, 0x07 }; + static unsigned char n3[6] = + {0x61, 0x68, 0x65, 0x0a, 0x60, 0x04}; + static unsigned char n4[0x46] = + {0x09, 0x01, 0x12, 0x04, 0x66, 0x8a, 0x80, 0x3c, + 0x81, 0x22, 0x84, 0x50, 0x8a, 0x78, 0x8b, 0x68, + 0x8c, 0x88, 0x8e, 0x33, 0x8f, 0x24, 0xaa, 0xb1, + 0xa2, 0x60, 0xa5, 0x30, 0xa6, 0x3a, 0xa8, 0xe8, + 0xae, 0x05, 0xb1, 0x00, 0xbb, 0x04, 0xbc, 0x48, + 0xbe, 0x36, 0xc6, 0x88, 0xe9, 0x00, 0xc5, 0xc0, + 0x65, 0x0a, 0xbb, 0x86, 0xaf, 0x58, 0xb0, 0x68, + 0x87, 0x40, 0x89, 0x2b, 0x8d, 0xff, 0x83, 0x40, + 0xac, 0x84, 0xad, 0x86, 0xaf, 0x46}; + static unsigned char nset4[18] = { + 0xe0, 0x60, 0xe1, 0xa8, 0xe2, 0xe0, 0xe3, 0x60, 0xe4, 0xa8, + 0xe5, 0xe0, 0xe6, 0x60, 0xe7, 0xa8, + 0xe8, 0xe0 + }; + /* ojo puede ser 0xe6 en vez de 0xe9 */ + static unsigned char nset2[20] = { + 0xd0, 0xbb, 0xd1, 0x28, 0xd2, 0x10, 0xd3, 0x10, 0xd4, 0xbb, + 0xd5, 0x28, 0xd6, 0x1e, 0xd7, 0x27, + 0xd8, 0xc8, 0xd9, 0xfc + }; + static unsigned char missing[8] = + { 0x87, 0x20, 0x88, 0x20, 0x89, 0x20, 0x80, 0x38 }; + static unsigned char nset3[18] = { + 0xc7, 0x60, 0xc8, 0xa8, 0xc9, 0xe0, 0xca, 0x60, 0xcb, 0xa8, + 0xcc, 0xe0, 0xcd, 0x60, 0xce, 0xa8, + 0xcf, 0xe0 + }; + static unsigned char nset5[4] = + { 0x8f, 0x24, 0xc3, 0x00 }; /* bright */ + static unsigned char nset6[34] = { + 0x90, 0x00, 0x91, 0x1c, 0x92, 0x30, 0x93, 0x43, 0x94, 0x54, + 0x95, 0x65, 0x96, 0x75, 0x97, 0x84, + 0x98, 0x93, 0x99, 0xa1, 0x9a, 0xb0, 0x9b, 0xbd, 0x9c, 0xca, + 0x9d, 0xd8, 0x9e, 0xe5, 0x9f, 0xf2, + 0xa0, 0xff + }; /* Gamma */ + static unsigned char nset7[4] = + { 0x66, 0xca, 0xa8, 0xf8 }; /* 50/60 Hz */ + static unsigned char nset9[4] = + { 0x0b, 0x04, 0x0a, 0x78 }; + static unsigned char nset8[6] = + { 0xa8, 0xf0, 0xc6, 0x88, 0xc0, 0x00 }; + static unsigned char nset10[6] = + { 0x0c, 0x03, 0xab, 0x10, 0x81, 0x20 }; + + t16RegWrite(dev, 0x01, 0x0000, n1, 0x06); + t16RegWrite(dev, 0x01, 0x0000, nset, 0x06); + t16RegRead(dev, 0x0063, &test_byte, 1); + t16RegWrite(dev, 0x01, 0x0000, n2, 0x02); + + while (read_indexs[i] != 0x00) { + t16RegRead(dev, read_indexs[i], &test_byte, 1); + PDEBUG(D_CONF, "Reg 0x%x => 0x%x", read_indexs[i], + test_byte); + i++; + } + + t16RegWrite(dev, 0x01, 0x0000, n3, 0x06); + t16RegWrite(dev, 0x01, 0x0000, n4, 0x46); + t16RegRead(dev, 0x0080, &test_byte, 1); + t16RegWrite(dev, 0x00, 0x2c80, 0x00, 0x0); + t16RegWrite(dev, 0x01, 0x0000, nset2, 0x14); + t16RegWrite(dev, 0x01, 0x0000, nset3, 0x12); + t16RegWrite(dev, 0x01, 0x0000, nset4, 0x12); + t16RegWrite(dev, 0x00, 0x3880, 0x00, 0x0); + t16RegWrite(dev, 0x00, 0x3880, 0x00, 0x0); + t16RegWrite(dev, 0x00, 0x338e, 0x00, 0x0); + t16RegWrite(dev, 0x01, 00, nset5, 0x04); + t16RegWrite(dev, 0x00, 0x00a9, 0x00, 0x0); + t16RegWrite(dev, 0x01, 00, nset6, 0x22); + t16RegWrite(dev, 0x00, 0x86bb, 0x00, 0x0); + t16RegWrite(dev, 0x00, 0x4aa6, 0x00, 0x0); + + t16RegWrite(dev, 0x01, 00, missing, 0x08); + + t16RegWrite(dev, 0x00, 0x2087, 0x00, 0x0); + t16RegWrite(dev, 0x00, 0x2088, 0x00, 0x0); + t16RegWrite(dev, 0x00, 0x2089, 0x00, 0x0); + + t16RegWrite(dev, 0x01, 00, nset7, 0x4); + t16RegWrite(dev, 0x01, 00, nset10, 0x06); + t16RegWrite(dev, 0x01, 00, nset8, 0x06); + t16RegWrite(dev, 0x01, 00, nset9, 0x04); + + t16RegWrite(dev, 0x00, 0x2880, 0x00, 0x0); + t16RegWrite(dev, 0x01, 0x0000, nset2, 0x14); + t16RegWrite(dev, 0x01, 0x0000, nset3, 0x12); + t16RegWrite(dev, 0x01, 0x0000, nset4, 0x12); + + return 0; +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + unsigned int brightness; + unsigned char set6[4] = { 0x8f, 0x26, 0xc3, 0x80 }; + brightness = sd->brightness; + + if (brightness < 7) { + set6[3] = 0x70 - (brightness * 0xa); + } else { + set6[1] = 0x24; + set6[3] = 0x00 + ((brightness - 7) * 0xa); + } + + t16RegWrite(dev, 0x01, 0x0000, set6, 4); +} + +static void setflip(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + + unsigned char flipcmd[8] = + { 0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09 }; + + if (sd->mirror == 1) + flipcmd[3] = 0x01; + + t16RegWrite(dev, 0x01, 0x0000, flipcmd, 8); +} + +static void seteffect(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + + t16RegWrite(dev, 0x01, 0x0000, effects_table[sd->effect], 0x06); + if (sd->effect == 1 || sd->effect == 5) { + PDEBUG(D_CONF, + "This effect have been disabled for webcam \"safety\""); + return; + } + + if (sd->effect == 1 || sd->effect == 4) + t16RegWrite(dev, 0x00, 0x4aa6, 0x00, 0x00); + else + t16RegWrite(dev, 0x00, 0xfaa6, 0x00, 0x00); +} + +static void setwhitebalance(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + + unsigned char white_balance[8] = + { 0x87, 0x20, 0x88, 0x20, 0x89, 0x20, 0x80, 0x38 }; + + if (sd->whitebalance == 1) + white_balance[7] = 0x3c; + + t16RegWrite(dev, 0x01, 0x0000, white_balance, 8); +} + +static void setlightfreq(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + __u8 freq[4] = { 0x66, 0x40, 0xa8, 0xe8 }; + + if (sd->freq == 2) /* 60hz */ + freq[1] = 0x00; + + t16RegWrite(dev, 0x1, 0x0000, freq, 0x4); +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + unsigned int contrast = sd->contrast; + __u16 reg_to_write = 0x00; + + if (contrast < 7) + reg_to_write = 0x8ea9 - (0x200 * contrast); + else + reg_to_write = (0x00a9 + ((contrast - 7) * 0x200)); + + t16RegWrite(dev, 0x00, reg_to_write, 0x00, 0); + +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + __u16 reg_to_write = 0x00; + + reg_to_write = 0xc0bb + sd->colors * 0x100; + t16RegWrite(dev, 0x00, reg_to_write, 0x00, 0); +} + +static void setgamma(struct gspca_dev *gspca_dev) +{ +} + +static void setsharpness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + __u16 reg_to_write = 0x00; + + reg_to_write = 0x0aa6 + 0x1000 * sd->sharpness; + + t16RegWrite(dev, 0x00, reg_to_write, 0x00, 0x00); +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->brightness; + return *val; +} + +static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->whitebalance = val; + if (gspca_dev->streaming) + setwhitebalance(gspca_dev); + return 0; +} + +static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->whitebalance; + return *val; +} + + +static int sd_setflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->mirror = val; + if (gspca_dev->streaming) + setflip(gspca_dev); + return 0; +} + +static int sd_getflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->mirror; + return *val; +} + +static int sd_seteffect(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->effect = val; + if (gspca_dev->streaming) + seteffect(gspca_dev); + return 0; +} + +static int sd_geteffect(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->effect; + return *val; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->contrast; + return *val; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->colors; + return 0; +} + +static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gamma = val; + if (gspca_dev->streaming) + setgamma(gspca_dev); + return 0; +} + +static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + *val = sd->gamma; + return 0; +} + +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->freq = val; + if (gspca_dev->streaming) + setlightfreq(gspca_dev); + return 0; +} + +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->freq; + return 0; +} + +static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->sharpness = val; + if (gspca_dev->streaming) + setsharpness(gspca_dev); + return 0; +} + +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->sharpness; + return 0; +} + +/* Low Light set here......*/ +static int sd_setlowlight(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + + sd->autogain = val; + if (val != 0) + t16RegWrite(dev, 0x00, 0xf48e, 0x00, 0); + else + t16RegWrite(dev, 0x00, 0xb48e, 0x00, 0); + return 0; +} + +static int sd_getlowlight(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + int mode; + __u8 test_byte; + + static __u8 t1[] = { 0x66, 0x00, 0xa8, 0xe8 }; + __u8 t2[] = { 0x07, 0x00, 0x0d, 0x60, 0x0e, 0x80 }; + static __u8 t3[] = + { 0xb3, 0x07, 0xb4, 0x00, 0xb5, 0x88, 0xb6, 0x02, 0xb7, 0x06, + 0xb8, 0x00, 0xb9, 0xe7, 0xba, 0x01 }; + static __u8 t4[] = { 0x0b, 0x04, 0x0a, 0x40 }; + + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode]. mode; + switch (mode) { + case 1: /* 352x288 */ + t2[1] = 0x40; + break; + case 2: /* 320x240 */ + t2[1] = 0x10; + break; + case 3: /* 176x144 */ + t2[1] = 0x50; + break; + case 4: /* 160x120 */ + t2[1] = 0x20; + break; + default: /* 640x480 (0x00) */ + break; + } + + t16RegWrite(dev, 0x01, 0x0000, tas5130a_sensor_init[0], 0x8); + t16RegWrite(dev, 0x01, 0x0000, tas5130a_sensor_init[1], 0x8); + t16RegWrite(dev, 0x01, 0x0000, tas5130a_sensor_init[2], 0x8); + t16RegWrite(dev, 0x01, 0x0000, tas5130a_sensor_init[3], 0x8); + t16RegWrite(dev, 0x00, 0x3c80, 0x00, 0x00); + /* just in case and to keep sync with logs (for mine) */ + t16RegWrite(dev, 0x01, 0x0000, tas5130a_sensor_init[3], 0x8); + t16RegWrite(dev, 0x00, 0x3c80, 0x00, 0x00); + /* just in case and to keep sync with logs (for mine) */ + t16RegWrite(dev, 0x01, 0x0000, t1, 4); + t16RegWrite(dev, 0x01, 0x0000, t2, 6); + t16RegRead(dev, 0x0012, &test_byte, 0x1); + t16RegWrite(dev, 0x01, 0x0000, t3, 0x10); + t16RegWrite(dev, 0x00, 0x0013, 0x00, 0x00); + t16RegWrite(dev, 0x01, 0x0000, t4, 0x4); + /* restart on each start, just in case, sometimes regs goes wrong + * when using controls from app */ + setbrightness(gspca_dev); + setcontrast(gspca_dev); + setcolors(gspca_dev); +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + unsigned char *data, /* isoc packet */ + int len) /* iso packet length */ +{ + int sof = 0; + static unsigned char ffd9[] = { 0xff, 0xd9 }; + + if (data[0] == 0x5a) { + /* Control Packet, after this came the header again, + * but extra bytes came in the packet before this, + * sometimes an EOF arrives, sometimes not... */ + return; + } + + if (data[len - 1] == 0xff && data[len] == 0xd9) { + /* Just in case, i have seen packets with the marker, + * other's do not include it... */ + data += 2; + len -= 4; + } else if (data[2] == 0xff && data[3] == 0xd8) { + sof = 1; + data += 2; + len -= 2; + } else { + data += 2; + len -= 2; + } + + if (sof) { + /* extra bytes....., could be processed too but would be + * a waste of time, right now leave the application and + * libjpeg do it for ourserlves.. */ + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + ffd9, 2); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len); + return; + } + + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); +} + +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + memset(menu->name, 0, sizeof menu->name); + + switch (menu->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (menu->index) { + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + strcpy(menu->name, "50 Hz"); + return 0; + case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ + strcpy(menu->name, "60 Hz"); + return 0; + } + break; + case V4L2_CID_EFFECTS: + if (menu->index < 0 || menu->index >= NUM_EFFECTS_CONTROL) + return -EINVAL; + strncpy((char *) menu->name, + effects_control[menu->index].name, 32); + break; + } + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + init_default_parameters(gspca_dev); + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, + .querymenu = sd_querymenu, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x17a1, 0x0128), DVNM("XPX Webcam")}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/tv8532.c b/drivers/media/video/gspca/tv8532.c new file mode 100644 index 000000000000..6218441ba1f0 --- /dev/null +++ b/drivers/media/video/gspca/tv8532.c @@ -0,0 +1,709 @@ +/* + * Quickcam cameras initialization data + * + * V4L2 by Jean-Francois Moine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#define MODULE_NAME "tv8532" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("TV8532 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + int buflen; /* current length of tmpbuf */ + __u8 tmpbuf[352 * 288 + 10 * 288]; /* no protection... */ + __u8 tmpbuf2[352 * 288]; /* no protection... */ + + unsigned short brightness; + unsigned short contrast; + + char packet; + char synchro; +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_BRIGHTNESS 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 1, + .maximum = 0x2ff, + .step = 1, + .default_value = 0x18f, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define SD_CONTRAST 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 0xffff, + .step = 1, + .default_value = 0x7fff, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +}; + +static struct cam_mode sif_mode[] = { + {V4L2_PIX_FMT_SBGGR8, 176, 144, 1}, + {V4L2_PIX_FMT_SBGGR8, 352, 288, 0}, +}; + +/* + * Initialization data: this is the first set-up data written to the + * device (before the open data). + */ +#define TESTCLK 0x10 /* reg 0x2c -> 0x12 //10 */ +#define TESTCOMP 0x90 /* reg 0x28 -> 0x80 */ +#define TESTLINE 0x81 /* reg 0x29 -> 0x81 */ +#define QCIFLINE 0x41 /* reg 0x29 -> 0x81 */ +#define TESTPTL 0x14 /* reg 0x2D -> 0x14 */ +#define TESTPTH 0x01 /* reg 0x2E -> 0x01 */ +#define TESTPTBL 0x12 /* reg 0x2F -> 0x0a */ +#define TESTPTBH 0x01 /* reg 0x30 -> 0x01 */ +#define ADWIDTHL 0xe8 /* reg 0x0c -> 0xe8 */ +#define ADWIDTHH 0x03 /* reg 0x0d -> 0x03 */ +#define ADHEIGHL 0x90 /* reg 0x0e -> 0x91 //93 */ +#define ADHEIGHH 0x01 /* reg 0x0f -> 0x01 */ +#define EXPOL 0x8f /* reg 0x1c -> 0x8f */ +#define EXPOH 0x01 /* reg 0x1d -> 0x01 */ +#define ADCBEGINL 0x44 /* reg 0x10 -> 0x46 //47 */ +#define ADCBEGINH 0x00 /* reg 0x11 -> 0x00 */ +#define ADRBEGINL 0x0a /* reg 0x14 -> 0x0b //0x0c */ +#define ADRBEGINH 0x00 /* reg 0x15 -> 0x00 */ +#define TV8532_CMD_UPDATE 0x84 + +#define TV8532_EEprom_Add 0x03 +#define TV8532_EEprom_DataL 0x04 +#define TV8532_EEprom_DataM 0x05 +#define TV8532_EEprom_DataH 0x06 +#define TV8532_EEprom_TableLength 0x07 +#define TV8532_EEprom_Write 0x08 +#define TV8532_PART_CTRL 0x00 +#define TV8532_CTRL 0x01 +#define TV8532_CMD_EEprom_Open 0x30 +#define TV8532_CMD_EEprom_Close 0x29 +#define TV8532_UDP_UPDATE 0x31 +#define TV8532_GPIO 0x39 +#define TV8532_GPIO_OE 0x3B +#define TV8532_REQ_RegWrite 0x02 +#define TV8532_REQ_RegRead 0x03 + +#define TV8532_ADWIDTH_L 0x0C +#define TV8532_ADWIDTH_H 0x0D +#define TV8532_ADHEIGHT_L 0x0E +#define TV8532_ADHEIGHT_H 0x0F +#define TV8532_EXPOSURE 0x1C +#define TV8532_QUANT_COMP 0x28 +#define TV8532_MODE_PACKET 0x29 +#define TV8532_SETCLK 0x2C +#define TV8532_POINT_L 0x2D +#define TV8532_POINT_H 0x2E +#define TV8532_POINTB_L 0x2F +#define TV8532_POINTB_H 0x30 +#define TV8532_BUDGET_L 0x2A +#define TV8532_BUDGET_H 0x2B +#define TV8532_VID_L 0x34 +#define TV8532_VID_H 0x35 +#define TV8532_PID_L 0x36 +#define TV8532_PID_H 0x37 +#define TV8532_DeviceID 0x83 +#define TV8532_AD_SLOPE 0x91 +#define TV8532_AD_BITCTRL 0x94 +#define TV8532_AD_COLBEGIN_L 0x10 +#define TV8532_AD_COLBEGIN_H 0x11 +#define TV8532_AD_ROWBEGIN_L 0x14 +#define TV8532_AD_ROWBEGIN_H 0x15 + +static __u32 tv_8532_eeprom_data[] = { +/* add dataL dataM dataH */ + 0x00010001, 0x01018011, 0x02050014, 0x0305001c, + 0x040d001e, 0x0505001f, 0x06050519, 0x0705011b, + 0x0805091e, 0x090d892e, 0x0a05892f, 0x0b050dd9, + 0x0c0509f1, 0 +}; + +static void reg_r(struct usb_device *dev, + __u16 index, __u8 *buffer) +{ + usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + TV8532_REQ_RegRead, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, buffer, sizeof(__u8), + 500); +} + +static void reg_w(struct usb_device *dev, + __u16 index, __u8 *buffer, __u16 length) +{ + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + TV8532_REQ_RegWrite, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, buffer, length, 500); +} + +static void tv_8532WriteEEprom(struct gspca_dev *gspca_dev) +{ + int i = 0; + __u8 reg, data0, data1, data2, datacmd; + struct usb_device *dev = gspca_dev->dev; + + datacmd = 0xb0;; + reg_w(dev, TV8532_GPIO, &datacmd, 1); + datacmd = TV8532_CMD_EEprom_Open; + reg_w(dev, TV8532_CTRL, &datacmd, + 1); +/* msleep(1); */ + while (tv_8532_eeprom_data[i]) { + reg = (tv_8532_eeprom_data[i] & 0xff000000) >> 24; + reg_w(dev, TV8532_EEprom_Add, ®, 1); + /* msleep(1); */ + data0 = (tv_8532_eeprom_data[i] & 0x000000ff); + reg_w(dev, TV8532_EEprom_DataL, &data0, 1); + /* msleep(1); */ + data1 = (tv_8532_eeprom_data[i] & 0x0000FF00) >> 8; + reg_w(dev, TV8532_EEprom_DataM, &data1, 1); + /* msleep(1); */ + data2 = (tv_8532_eeprom_data[i] & 0x00FF0000) >> 16; + reg_w(dev, TV8532_EEprom_DataH, &data2, 1); + /* msleep(1); */ + datacmd = 0; + reg_w(dev, TV8532_EEprom_Write, &datacmd, 1); + /* msleep(10); */ + i++; + } + datacmd = i; + reg_w(dev, TV8532_EEprom_TableLength, &datacmd, 1); +/* msleep(1); */ + datacmd = TV8532_CMD_EEprom_Close; + reg_w(dev, TV8532_CTRL, &datacmd, 1); + msleep(10); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + tv_8532WriteEEprom(gspca_dev); + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 1; + cam->cam_mode = sif_mode; + cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; + + sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; + return 0; +} + +static void tv_8532ReadRegisters(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 data = 0; +/* __u16 vid, pid; */ + + reg_r(dev, 0x0001, &data); + PDEBUG(D_USBI, "register 0x01-> %x", data); + reg_r(dev, 0x0002, &data); + PDEBUG(D_USBI, "register 0x02-> %x", data); + reg_r(dev, TV8532_ADWIDTH_L, &data); + reg_r(dev, TV8532_ADWIDTH_H, &data); + reg_r(dev, TV8532_QUANT_COMP, &data); + reg_r(dev, TV8532_MODE_PACKET, &data); + reg_r(dev, TV8532_SETCLK, &data); + reg_r(dev, TV8532_POINT_L, &data); + reg_r(dev, TV8532_POINT_H, &data); + reg_r(dev, TV8532_POINTB_L, &data); + reg_r(dev, TV8532_POINTB_H, &data); + reg_r(dev, TV8532_BUDGET_L, &data); + reg_r(dev, TV8532_BUDGET_H, &data); + reg_r(dev, TV8532_VID_L, &data); + reg_r(dev, TV8532_VID_H, &data); + reg_r(dev, TV8532_PID_L, &data); + reg_r(dev, TV8532_PID_H, &data); + reg_r(dev, TV8532_DeviceID, &data); + reg_r(dev, TV8532_AD_COLBEGIN_L, &data); + reg_r(dev, TV8532_AD_COLBEGIN_H, &data); + reg_r(dev, TV8532_AD_ROWBEGIN_L, &data); + reg_r(dev, TV8532_AD_ROWBEGIN_H, &data); +} + +static void tv_8532_setReg(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 data = 0; + __u8 value[2] = { 0, 0 }; + + data = ADCBEGINL; + reg_w(dev, TV8532_AD_COLBEGIN_L, &data, 1); /* 0x10 */ + data = ADCBEGINH; /* also digital gain */ + reg_w(dev, TV8532_AD_COLBEGIN_H, &data, 1); + data = TV8532_CMD_UPDATE; + reg_w(dev, TV8532_PART_CTRL, &data, 1); /* 0x00<-0x84 */ + + data = 0x0a; + reg_w(dev, TV8532_GPIO_OE, &data, 1); + /******************************************************/ + data = ADHEIGHL; + reg_w(dev, TV8532_ADHEIGHT_L, &data, 1); /* 0e */ + data = ADHEIGHH; + reg_w(dev, TV8532_ADHEIGHT_H, &data, 1); /* 0f */ + value[0] = EXPOL; + value[1] = EXPOH; /* 350d 0x014c; */ + reg_w(dev, TV8532_EXPOSURE, value, 2); /* 1c */ + data = ADCBEGINL; + reg_w(dev, TV8532_AD_COLBEGIN_L, &data, 1); /* 0x10 */ + data = ADCBEGINH; /* also digital gain */ + reg_w(dev, TV8532_AD_COLBEGIN_H, &data, 1); + data = ADRBEGINL; + reg_w(dev, TV8532_AD_ROWBEGIN_L, &data, 1); /* 0x14 */ + + data = 0x00; + reg_w(dev, TV8532_AD_SLOPE, &data, 1); /* 0x91 */ + data = 0x02; + reg_w(dev, TV8532_AD_BITCTRL, &data, 1); /* 0x94 */ + + + data = TV8532_CMD_EEprom_Close; + reg_w(dev, TV8532_CTRL, &data, 1); /* 0x01 */ + + data = 0x00; + reg_w(dev, TV8532_AD_SLOPE, &data, 1); /* 0x91 */ + data = TV8532_CMD_UPDATE; + reg_w(dev, TV8532_PART_CTRL, &data, 1); /* 0x00<-0x84 */ +} + +static void tv_8532_PollReg(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 data = 0; + int i; + + /* strange polling from tgc */ + for (i = 0; i < 10; i++) { + data = TESTCLK; /* 0x48; //0x08; */ + reg_w(dev, TV8532_SETCLK, &data, 1); /* 0x2c */ + data = TV8532_CMD_UPDATE; + reg_w(dev, TV8532_PART_CTRL, &data, 1); + data = 0x01; + reg_w(dev, TV8532_UDP_UPDATE, &data, 1); /* 0x31 */ + } +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 data = 0; + __u8 dataStart = 0; + __u8 value[2] = { 0, 0 }; + + data = 0x32; + reg_w(dev, TV8532_AD_SLOPE, &data, 1); + data = 0; + reg_w(dev, TV8532_AD_BITCTRL, &data, 1); + tv_8532ReadRegisters(gspca_dev); + data = 0x0b; + reg_w(dev, TV8532_GPIO_OE, &data, 1); + value[0] = ADHEIGHL; + value[1] = ADHEIGHH; /* 401d 0x0169; */ + reg_w(dev, TV8532_ADHEIGHT_L, value, 2); /* 0e */ + value[0] = EXPOL; + value[1] = EXPOH; /* 350d 0x014c; */ + reg_w(dev, TV8532_EXPOSURE, value, 2); /* 1c */ + data = ADWIDTHL; /* 0x20; */ + reg_w(dev, TV8532_ADWIDTH_L, &data, 1); /* 0x0c */ + data = ADWIDTHH; + reg_w(dev, TV8532_ADWIDTH_H, &data, 1); /* 0x0d */ + + /*******************************************************************/ + data = TESTCOMP; /* 0x72 compressed mode */ + reg_w(dev, TV8532_QUANT_COMP, &data, 1); /* 0x28 */ + data = TESTLINE; /* 0x84; // CIF | 4 packet */ + reg_w(dev, TV8532_MODE_PACKET, &data, 1); /* 0x29 */ + + /************************************************/ + data = TESTCLK; /* 0x48; //0x08; */ + reg_w(dev, TV8532_SETCLK, &data, 1); /* 0x2c */ + data = TESTPTL; /* 0x38; */ + reg_w(dev, TV8532_POINT_L, &data, 1); /* 0x2d */ + data = TESTPTH; /* 0x04; */ + reg_w(dev, TV8532_POINT_H, &data, 1); /* 0x2e */ + dataStart = TESTPTBL; /* 0x04; */ + reg_w(dev, TV8532_POINTB_L, &dataStart, 1); /* 0x2f */ + data = TESTPTBH; /* 0x04; */ + reg_w(dev, TV8532_POINTB_H, &data, 1); /* 0x30 */ + data = TV8532_CMD_UPDATE; + reg_w(dev, TV8532_PART_CTRL, &data, 1); /* 0x00<-0x84 */ + /*************************************************/ + data = 0x01; + reg_w(dev, TV8532_UDP_UPDATE, &data, 1); /* 0x31 */ + msleep(200); + data = 0x00; + reg_w(dev, TV8532_UDP_UPDATE, &data, 1); /* 0x31 */ + /*************************************************/ + tv_8532_setReg(gspca_dev); + /*************************************************/ + data = 0x0b; + reg_w(dev, TV8532_GPIO_OE, &data, + 1); + /*************************************************/ + tv_8532_setReg(gspca_dev); + /*************************************************/ + tv_8532_PollReg(gspca_dev); + return 0; +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 value[2]; + __u8 data; + int brightness = sd->brightness; + + value[1] = (brightness >> 8) & 0xff; + value[0] = (brightness) & 0xff; + reg_w(gspca_dev->dev, TV8532_EXPOSURE, value, 2); /* 1c */ + data = TV8532_CMD_UPDATE; + reg_w(gspca_dev->dev, TV8532_PART_CTRL, &data, 1); +} + +/* -- start the camera -- */ +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 data = 0; + __u8 dataStart = 0; + __u8 value[2]; + + data = 0x32; + reg_w(dev, TV8532_AD_SLOPE, &data, 1); + data = 0; + reg_w(dev, TV8532_AD_BITCTRL, &data, 1); + tv_8532ReadRegisters(gspca_dev); + data = 0x0b; + reg_w(dev, TV8532_GPIO_OE, &data, 1); + value[0] = ADHEIGHL; + value[1] = ADHEIGHH; /* 401d 0x0169; */ + reg_w(dev, TV8532_ADHEIGHT_L, value, 2); /* 0e */ +/* value[0] = EXPOL; value[1] =EXPOH; * 350d 0x014c; */ +/* reg_w(dev,TV8532_REQ_RegWrite,0,TV8532_EXPOSURE,value,2); * 1c */ + setbrightness(gspca_dev); + + data = ADWIDTHL; /* 0x20; */ + reg_w(dev, TV8532_ADWIDTH_L, &data, 1); /* 0x0c */ + data = ADWIDTHH; + reg_w(dev, TV8532_ADWIDTH_H, &data, 1); /* 0x0d */ + + /************************************************/ + data = TESTCOMP; /* 0x72 compressed mode */ + reg_w(dev, TV8532_QUANT_COMP, &data, 1); /* 0x28 */ + if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode) { + /* 176x144 */ + data = QCIFLINE; /* 0x84; // CIF | 4 packet */ + reg_w(dev, TV8532_MODE_PACKET, &data, 1); /* 0x29 */ + } else { + /* 352x288 */ + data = TESTLINE; /* 0x84; // CIF | 4 packet */ + reg_w(dev, TV8532_MODE_PACKET, &data, 1); /* 0x29 */ + } + /************************************************/ + data = TESTCLK; /* 0x48; //0x08; */ + reg_w(dev, TV8532_SETCLK, &data, 1); /* 0x2c */ + data = TESTPTL; /* 0x38; */ + reg_w(dev, TV8532_POINT_L, &data, 1); /* 0x2d */ + data = TESTPTH; /* 0x04; */ + reg_w(dev, TV8532_POINT_H, &data, 1); /* 0x2e */ + dataStart = TESTPTBL; /* 0x04; */ + reg_w(dev, TV8532_POINTB_L, &dataStart, 1); /* 0x2f */ + data = TESTPTBH; /* 0x04; */ + reg_w(dev, TV8532_POINTB_H, &data, 1); /* 0x30 */ + data = TV8532_CMD_UPDATE; + reg_w(dev, TV8532_PART_CTRL, &data, 1); /* 0x00<-0x84 */ + /************************************************/ + data = 0x01; + reg_w(dev, TV8532_UDP_UPDATE, &data, 1); /* 0x31 */ + msleep(200); + data = 0x00; + reg_w(dev, TV8532_UDP_UPDATE, &data, 1); /* 0x31 */ + /************************************************/ + tv_8532_setReg(gspca_dev); + /************************************************/ + data = 0x0b; + reg_w(dev, TV8532_GPIO_OE, &data, 1); + /************************************************/ + tv_8532_setReg(gspca_dev); + /************************************************/ + tv_8532_PollReg(gspca_dev); + data = 0x00; + reg_w(dev, TV8532_UDP_UPDATE, &data, 1); /* 0x31 */ +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + __u8 data; + + data = 0x0b; + reg_w(dev, TV8532_GPIO_OE, &data, 1); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ +} + +static void sd_close(struct gspca_dev *gspca_dev) +{ +} + +static void tv8532_preprocess(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; +/* we should received a whole frame with header and EOL marker + * in gspca_dev->tmpbuf and return a GBRG pattern in gspca_dev->tmpbuf2 + * sequence 2bytes header the Alternate pixels bayer GB 4 bytes + * Alternate pixels bayer RG 4 bytes EOL */ + int width = gspca_dev->width; + int height = gspca_dev->height; + unsigned char *dst = sd->tmpbuf2; + unsigned char *data = sd->tmpbuf; + int i; + + /* precompute where is the good bayer line */ + if (((data[3] + data[width + 7]) >> 1) + + (data[4] >> 2) + + (data[width + 6] >> 1) >= ((data[2] + data[width + 6]) >> 1) + + (data[3] >> 2) + + (data[width + 5] >> 1)) + data += 3; + else + data += 2; + for (i = 0; i < height / 2; i++) { + memcpy(dst, data, width); + data += width + 3; + dst += width; + memcpy(dst, data, width); + data += width + 7; + dst += width; + } +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (data[0] != 0x80) { + sd->packet++; + if (sd->buflen + len > sizeof sd->tmpbuf) { + if (gspca_dev->last_packet_type != DISCARD_PACKET) { + PDEBUG(D_PACK, "buffer overflow"); + gspca_dev->last_packet_type = DISCARD_PACKET; + } + return; + } + memcpy(&sd->tmpbuf[sd->buflen], data, len); + sd->buflen += len; + return; + } + + /* here we detect 0x80 */ + /* counter is limited so we need few header for a frame :) */ + + /* header 0x80 0x80 0x80 0x80 0x80 */ + /* packet 00 63 127 145 00 */ + /* sof 0 1 1 0 0 */ + + /* update sequence */ + if (sd->packet == 63 || sd->packet == 127) + sd->synchro = 1; + + /* is there a frame start ? */ + if (sd->packet >= (gspca_dev->height >> 1) - 1) { + PDEBUG(D_PACK, "SOF > %d packet %d", sd->synchro, + sd->packet); + if (!sd->synchro) { /* start of frame */ + if (gspca_dev->last_packet_type == FIRST_PACKET) { + tv8532_preprocess(gspca_dev); + frame = gspca_frame_add(gspca_dev, + LAST_PACKET, + frame, sd->tmpbuf2, + gspca_dev->width * + gspca_dev->width); + } + gspca_frame_add(gspca_dev, FIRST_PACKET, + frame, data, 0); + memcpy(sd->tmpbuf, data, len); + sd->buflen = len; + sd->packet = 0; + return; + } + if (gspca_dev->last_packet_type != DISCARD_PACKET) { + PDEBUG(D_PACK, + "Warning wrong TV8532 frame detection %d", + sd->packet); + gspca_dev->last_packet_type = DISCARD_PACKET; + } + return; + } + + if (!sd->synchro) { + /* Drop packet frame corrupt */ + PDEBUG(D_PACK, "DROP SOF %d packet %d", + sd->synchro, sd->packet); + sd->packet = 0; + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + sd->synchro = 1; + sd->packet++; + memcpy(&sd->tmpbuf[sd->buflen], data, len); + sd->buflen += len; +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->contrast; + return 0; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x046d, 0x0920), DVNM("QC Express")}, + {USB_DEVICE(0x046d, 0x0921), DVNM("Labtec Webcam")}, + {USB_DEVICE(0x0545, 0x808b), DVNM("Veo Stingray")}, + {USB_DEVICE(0x0545, 0x8333), DVNM("Veo Stingray")}, + {USB_DEVICE(0x0923, 0x010f), DVNM("ICM532 cams")}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} + +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c new file mode 100644 index 000000000000..8b5f6d17d2a5 --- /dev/null +++ b/drivers/media/video/gspca/vc032x.c @@ -0,0 +1,1816 @@ +/* + * Z-star vc0321 library + * Copyright (C) 2006 Koninski Artur takeshi87@o2.pl + * Copyright (C) 2006 Michel Xhaard + * + * V4L2 by Jean-Francois Moine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "vc032x" + +#include "gspca.h" + +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; + +MODULE_AUTHOR("Michel Xhaard "); +MODULE_DESCRIPTION("GSPCA/VC032X USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + unsigned char autogain; + unsigned char lightfreq; + + char qindex; + char bridge; +#define BRIDGE_VC0321 0 +#define BRIDGE_VC0323 1 + char sensor; +#define SENSOR_HV7131R 0 +#define SENSOR_MI1320 1 +#define SENSOR_MI1310_SOC 2 +#define SENSOR_OV7660 3 +#define SENSOR_OV7670 4 +#define SENSOR_PO3130NC 5 +}; + +/* V4L2 controls supported by the driver */ +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +#define SD_AUTOGAIN 0 + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +#define SD_FREQ 1 + { + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light frequency filter", + .minimum = 0, + .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ + .step = 1, + .default_value = 1, + }, + .set = sd_setfreq, + .get = sd_getfreq, + }, +}; + +static struct cam_mode vc0321_mode[] = { + {V4L2_PIX_FMT_YUYV, 320, 240, 1}, + {V4L2_PIX_FMT_YUYV, 640, 480, 0}, +}; +static struct cam_mode vc0323_mode[] = { + {V4L2_PIX_FMT_JPEG, 320, 240, 1}, + {V4L2_PIX_FMT_JPEG, 640, 480, 0}, +}; + +static __u8 mi1310_socinitVGA_JPG[][4] = { + {0xb0, 0x03, 0x19, 0xcc}, + {0xb0, 0x04, 0x02, 0xcc}, + {0xb3, 0x00, 0x64, 0xcc}, + {0xb3, 0x00, 0x65, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x00, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x35, 0xdd, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x03, 0xcc}, + {0xb3, 0x23, 0xc0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x04, 0xcc}, + {0xb3, 0x17, 0xff, 0xcc}, + {0xb3, 0x00, 0x65, 0xcc}, + {0xb8, 0x00, 0x00, 0xcc}, + {0xbc, 0x00, 0xd0, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0xc8, 0x9f, 0x0b, 0xbb}, + {0x5b, 0x00, 0x01, 0xbb}, + {0x2f, 0xde, 0x20, 0xbb}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x20, 0x03, 0x02, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x05, 0x00, 0x07, 0xbb}, + {0x34, 0x00, 0x00, 0xbb}, + {0x35, 0xff, 0x00, 0xbb}, + {0xdc, 0x07, 0x02, 0xbb}, + {0xdd, 0x3c, 0x18, 0xbb}, + {0xde, 0x92, 0x6d, 0xbb}, + {0xdf, 0xcd, 0xb1, 0xbb}, + {0xe0, 0xff, 0xe7, 0xbb}, + {0x06, 0xf0, 0x0d, 0xbb}, + {0x06, 0x70, 0x0e, 0xbb}, + {0x4c, 0x00, 0x01, 0xbb}, + {0x4d, 0x00, 0x01, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x2e, 0x0c, 0x55, 0xbb}, + {0x21, 0xb6, 0x6e, 0xbb}, + {0x36, 0x30, 0x10, 0xbb}, + {0x37, 0x00, 0xc1, 0xbb}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x07, 0x00, 0x84, 0xbb}, + {0x08, 0x02, 0x4a, 0xbb}, + {0x05, 0x01, 0x10, 0xbb}, + {0x06, 0x00, 0x39, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x58, 0x02, 0x67, 0xbb}, + {0x57, 0x02, 0x00, 0xbb}, + {0x5a, 0x02, 0x67, 0xbb}, + {0x59, 0x02, 0x00, 0xbb}, + {0x5c, 0x12, 0x0d, 0xbb}, + {0x5d, 0x16, 0x11, 0xbb}, + {0x39, 0x06, 0x18, 0xbb}, + {0x3a, 0x06, 0x18, 0xbb}, + {0x3b, 0x06, 0x18, 0xbb}, + {0x3c, 0x06, 0x18, 0xbb}, + {0x64, 0x7b, 0x5b, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x36, 0x30, 0x10, 0xbb}, + {0x37, 0x00, 0xc0, 0xbb}, + {0xbc, 0x0e, 0x00, 0xcc}, + {0xbc, 0x0f, 0x05, 0xcc}, + {0xbc, 0x10, 0xc0, 0xcc}, + {0xbc, 0x11, 0x03, 0xcc}, + {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x02, 0xcc}, + {0xb6, 0x02, 0x80, 0xcc}, + {0xb6, 0x05, 0x01, 0xcc}, + {0xb6, 0x04, 0xe0, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb6, 0x13, 0x25, 0xcc}, + {0xb6, 0x18, 0x02, 0xcc}, + {0xb6, 0x17, 0x58, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, + {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, + {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, + {0xbf, 0xcc, 0x00, 0xcc}, + {0xbc, 0x02, 0x18, 0xcc}, + {0xbc, 0x03, 0x50, 0xcc}, + {0xbc, 0x04, 0x18, 0xcc}, + {0xbc, 0x05, 0x00, 0xcc}, + {0xbc, 0x06, 0x00, 0xcc}, + {0xbc, 0x08, 0x30, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, + {0xbc, 0x0a, 0x10, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, + {0xbc, 0x0c, 0x00, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x80, 0x00, 0x03, 0xbb}, + {0x81, 0xc7, 0x14, 0xbb}, + {0x82, 0xeb, 0xe8, 0xbb}, + {0x83, 0xfe, 0xf4, 0xbb}, + {0x84, 0xcd, 0x10, 0xbb}, + {0x85, 0xf3, 0xee, 0xbb}, + {0x86, 0xff, 0xf1, 0xbb}, + {0x87, 0xcd, 0x10, 0xbb}, + {0x88, 0xf3, 0xee, 0xbb}, + {0x89, 0x01, 0xf1, 0xbb}, + {0x8a, 0xe5, 0x17, 0xbb}, + {0x8b, 0xe8, 0xe2, 0xbb}, + {0x8c, 0xf7, 0xed, 0xbb}, + {0x8d, 0x00, 0xff, 0xbb}, + {0x8e, 0xec, 0x10, 0xbb}, + {0x8f, 0xf0, 0xed, 0xbb}, + {0x90, 0xf9, 0xf2, 0xbb}, + {0x91, 0x00, 0x00, 0xbb}, + {0x92, 0xe9, 0x0d, 0xbb}, + {0x93, 0xf4, 0xf2, 0xbb}, + {0x94, 0xfb, 0xf5, 0xbb}, + {0x95, 0x00, 0xff, 0xbb}, + {0xb6, 0x0f, 0x08, 0xbb}, + {0xb7, 0x3d, 0x16, 0xbb}, + {0xb8, 0x0c, 0x04, 0xbb}, + {0xb9, 0x1c, 0x07, 0xbb}, + {0xba, 0x0a, 0x03, 0xbb}, + {0xbb, 0x1b, 0x09, 0xbb}, + {0xbc, 0x17, 0x0d, 0xbb}, + {0xbd, 0x23, 0x1d, 0xbb}, + {0xbe, 0x00, 0x28, 0xbb}, + {0xbf, 0x11, 0x09, 0xbb}, + {0xc0, 0x16, 0x15, 0xbb}, + {0xc1, 0x00, 0x1b, 0xbb}, + {0xc2, 0x0e, 0x07, 0xbb}, + {0xc3, 0x14, 0x10, 0xbb}, + {0xc4, 0x00, 0x17, 0xbb}, + {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x06, 0xf4, 0x8e, 0xbb}, + {0x00, 0x00, 0x50, 0xdd}, + {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x24, 0x50, 0x20, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x34, 0x0c, 0x50, 0xbb}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x03, 0x03, 0xc0, 0xbb}, + {}, +}; +static __u8 mi1310_socinitQVGA_JPG[][4] = { + {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc}, + {0xb3, 0x00, 0x64, 0xcc}, {0xb3, 0x00, 0x65, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, {0xb3, 0x06, 0x00, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, {0xb3, 0x35, 0xdd, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x03, 0xcc}, + {0xb3, 0x23, 0xc0, 0xcc}, {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, {0xb3, 0x16, 0x04, 0xcc}, + {0xb3, 0x17, 0xff, 0xcc}, {0xb3, 0x00, 0x65, 0xcc}, + {0xb8, 0x00, 0x00, 0xcc}, {0xbc, 0x00, 0xf0, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, {0xf0, 0x00, 0x02, 0xbb}, + {0xc8, 0x9f, 0x0b, 0xbb}, {0x5b, 0x00, 0x01, 0xbb}, + {0x2f, 0xde, 0x20, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, + {0x20, 0x03, 0x02, 0xbb}, {0xf0, 0x00, 0x01, 0xbb}, + {0x05, 0x00, 0x07, 0xbb}, {0x34, 0x00, 0x00, 0xbb}, + {0x35, 0xff, 0x00, 0xbb}, {0xdc, 0x07, 0x02, 0xbb}, + {0xdd, 0x3c, 0x18, 0xbb}, {0xde, 0x92, 0x6d, 0xbb}, + {0xdf, 0xcd, 0xb1, 0xbb}, {0xe0, 0xff, 0xe7, 0xbb}, + {0x06, 0xf0, 0x0d, 0xbb}, {0x06, 0x70, 0x0e, 0xbb}, + {0x4c, 0x00, 0x01, 0xbb}, {0x4d, 0x00, 0x01, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x2e, 0x0c, 0x55, 0xbb}, + {0x21, 0xb6, 0x6e, 0xbb}, {0x36, 0x30, 0x10, 0xbb}, + {0x37, 0x00, 0xc1, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, + {0x07, 0x00, 0x84, 0xbb}, {0x08, 0x02, 0x4a, 0xbb}, + {0x05, 0x01, 0x10, 0xbb}, {0x06, 0x00, 0x39, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x58, 0x02, 0x67, 0xbb}, + {0x57, 0x02, 0x00, 0xbb}, {0x5a, 0x02, 0x67, 0xbb}, + {0x59, 0x02, 0x00, 0xbb}, {0x5c, 0x12, 0x0d, 0xbb}, + {0x5d, 0x16, 0x11, 0xbb}, {0x39, 0x06, 0x18, 0xbb}, + {0x3a, 0x06, 0x18, 0xbb}, {0x3b, 0x06, 0x18, 0xbb}, + {0x3c, 0x06, 0x18, 0xbb}, {0x64, 0x7b, 0x5b, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x36, 0x30, 0x10, 0xbb}, + {0x37, 0x00, 0xc0, 0xbb}, {0xbc, 0x0e, 0x00, 0xcc}, + {0xbc, 0x0f, 0x05, 0xcc}, {0xbc, 0x10, 0xc0, 0xcc}, + {0xbc, 0x11, 0x03, 0xcc}, {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x01, 0xcc}, {0xb6, 0x02, 0x40, 0xcc}, + {0xb6, 0x05, 0x00, 0xcc}, {0xb6, 0x04, 0xf0, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x25, 0xcc}, + {0xb6, 0x18, 0x00, 0xcc}, {0xb6, 0x17, 0x96, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, {0xbf, 0xcc, 0x00, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, {0xf0, 0x00, 0x01, 0xbb}, + {0x80, 0x00, 0x03, 0xbb}, {0x81, 0xc7, 0x14, 0xbb}, + {0x82, 0xeb, 0xe8, 0xbb}, {0x83, 0xfe, 0xf4, 0xbb}, + {0x84, 0xcd, 0x10, 0xbb}, {0x85, 0xf3, 0xee, 0xbb}, + {0x86, 0xff, 0xf1, 0xbb}, {0x87, 0xcd, 0x10, 0xbb}, + {0x88, 0xf3, 0xee, 0xbb}, {0x89, 0x01, 0xf1, 0xbb}, + {0x8a, 0xe5, 0x17, 0xbb}, {0x8b, 0xe8, 0xe2, 0xbb}, + {0x8c, 0xf7, 0xed, 0xbb}, {0x8d, 0x00, 0xff, 0xbb}, + {0x8e, 0xec, 0x10, 0xbb}, {0x8f, 0xf0, 0xed, 0xbb}, + {0x90, 0xf9, 0xf2, 0xbb}, {0x91, 0x00, 0x00, 0xbb}, + {0x92, 0xe9, 0x0d, 0xbb}, {0x93, 0xf4, 0xf2, 0xbb}, + {0x94, 0xfb, 0xf5, 0xbb}, {0x95, 0x00, 0xff, 0xbb}, + {0xb6, 0x0f, 0x08, 0xbb}, {0xb7, 0x3d, 0x16, 0xbb}, + {0xb8, 0x0c, 0x04, 0xbb}, {0xb9, 0x1c, 0x07, 0xbb}, + {0xba, 0x0a, 0x03, 0xbb}, {0xbb, 0x1b, 0x09, 0xbb}, + {0xbc, 0x17, 0x0d, 0xbb}, {0xbd, 0x23, 0x1d, 0xbb}, + {0xbe, 0x00, 0x28, 0xbb}, {0xbf, 0x11, 0x09, 0xbb}, + {0xc0, 0x16, 0x15, 0xbb}, {0xc1, 0x00, 0x1b, 0xbb}, + {0xc2, 0x0e, 0x07, 0xbb}, {0xc3, 0x14, 0x10, 0xbb}, + {0xc4, 0x00, 0x17, 0xbb}, {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, {0x06, 0xf4, 0x8e, 0xbb}, + {0x00, 0x00, 0x50, 0xdd}, {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x24, 0x50, 0x20, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x34, 0x0c, 0x50, 0xbb}, + {0xb3, 0x01, 0x41, 0xcc}, {0xf0, 0x00, 0x00, 0xbb}, + {0x03, 0x03, 0xc0, 0xbb}, + {}, +}; + +static __u8 mi1320_gamma[17] = { + 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, + 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff +}; +static __u8 mi1320_matrix[9] = { + 0x54, 0xda, 0x06, 0xf1, 0x50, 0xf4, 0xf7, 0xea, 0x52 +}; +static __u8 mi1320_initVGA_data[][4] = { + {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, + {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, + {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, + {0xb3, 0x00, 0x64, 0xcc}, {0xb3, 0x00, 0x65, 0xcc}, + {0xb0, 0x16, 0x03, 0xcc}, {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x00, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x35, 0xc8, 0xcc}, {0xb3, 0x02, 0x00, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x03, 0xcc}, {0xb3, 0x23, 0xc0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x04, 0xcc}, {0xb3, 0x17, 0xff, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, {0xbc, 0x00, 0xd0, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, {0xf0, 0x00, 0x00, 0xbb}, + {0x0d, 0x00, 0x09, 0xbb}, {0x00, 0x01, 0x00, 0xdd}, + {0x0d, 0x00, 0x08, 0xbb}, {0xf0, 0x00, 0x01, 0xbb}, + {0xa1, 0x05, 0x00, 0xbb}, {0xa4, 0x03, 0xc0, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x00, 0x00, 0x10, 0xdd}, + {0xc8, 0x9f, 0x0b, 0xbb}, {0x00, 0x00, 0x10, 0xdd}, + {0xf0, 0x00, 0x00, 0xbb}, {0x00, 0x00, 0x10, 0xdd}, + {0x20, 0x01, 0x00, 0xbb}, {0x00, 0x00, 0x10, 0xdd}, + {0xf0, 0x00, 0x01, 0xbb}, {0x9d, 0x3c, 0xa0, 0xbb}, + {0x47, 0x30, 0x30, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, + {0x0a, 0x80, 0x11, 0xbb}, {0x35, 0x00, 0x22, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x9d, 0xc5, 0x05, 0xbb}, + {0xdc, 0x0f, 0xfc, 0xbb}, {0xf0, 0x00, 0x01, 0xbb}, + {0x06, 0x74, 0x0e, 0xbb}, {0x80, 0x00, 0x06, 0xbb}, + {0x81, 0x04, 0x00, 0xbb}, {0x82, 0x01, 0x02, 0xbb}, + {0x83, 0x03, 0x02, 0xbb}, {0x84, 0x05, 0x00, 0xbb}, + {0x85, 0x01, 0x00, 0xbb}, {0x86, 0x03, 0x02, 0xbb}, + {0x87, 0x05, 0x00, 0xbb}, {0x88, 0x01, 0x00, 0xbb}, + {0x89, 0x02, 0x02, 0xbb}, {0x8a, 0xfd, 0x04, 0xbb}, + {0x8b, 0xfc, 0xfd, 0xbb}, {0x8c, 0xff, 0xfd, 0xbb}, + {0x8d, 0x00, 0x00, 0xbb}, {0x8e, 0xfe, 0x05, 0xbb}, + {0x8f, 0xfc, 0xfd, 0xbb}, {0x90, 0xfe, 0xfd, 0xbb}, + {0x91, 0x00, 0x00, 0xbb}, {0x92, 0xfe, 0x03, 0xbb}, + {0x93, 0xfd, 0xfe, 0xbb}, {0x94, 0xff, 0xfd, 0xbb}, + {0x95, 0x00, 0x00, 0xbb}, {0xb6, 0x07, 0x05, 0xbb}, + {0xb7, 0x13, 0x06, 0xbb}, {0xb8, 0x08, 0x06, 0xbb}, + {0xb9, 0x14, 0x08, 0xbb}, {0xba, 0x06, 0x05, 0xbb}, + {0xbb, 0x13, 0x06, 0xbb}, {0xbc, 0x03, 0x01, 0xbb}, + {0xbd, 0x03, 0x04, 0xbb}, {0xbe, 0x00, 0x02, 0xbb}, + {0xbf, 0x03, 0x01, 0xbb}, {0xc0, 0x02, 0x04, 0xbb}, + {0xc1, 0x00, 0x04, 0xbb}, {0xc2, 0x02, 0x01, 0xbb}, + {0xc3, 0x01, 0x03, 0xbb}, {0xc4, 0x00, 0x04, 0xbb}, + {0xf0, 0x00, 0x00, 0xbb}, {0x05, 0x01, 0x13, 0xbb}, + {0x06, 0x00, 0x11, 0xbb}, {0x07, 0x00, 0x85, 0xbb}, + {0x08, 0x00, 0x27, 0xbb}, {0x20, 0x01, 0x03, 0xbb}, + {0x21, 0x80, 0x00, 0xbb}, {0x22, 0x0d, 0x0f, 0xbb}, + {0x24, 0x80, 0x00, 0xbb}, {0x59, 0x00, 0xff, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x39, 0x03, 0x0d, 0xbb}, + {0x3a, 0x06, 0x1b, 0xbb}, {0x3b, 0x00, 0x95, 0xbb}, + {0x3c, 0x04, 0xdb, 0xbb}, {0x57, 0x02, 0x00, 0xbb}, + {0x58, 0x02, 0x66, 0xbb}, {0x59, 0x00, 0xff, 0xbb}, + {0x5a, 0x01, 0x33, 0xbb}, {0x5c, 0x12, 0x0d, 0xbb}, + {0x5d, 0x16, 0x11, 0xbb}, {0x64, 0x5e, 0x1c, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x2f, 0xd1, 0x00, 0xbb}, + {0x5b, 0x00, 0x01, 0xbb}, {0xf0, 0x00, 0x02, 0xbb}, + {0x36, 0x68, 0x10, 0xbb}, {0x00, 0x00, 0x30, 0xdd}, + {0x37, 0x82, 0x00, 0xbb}, {0xbc, 0x0e, 0x00, 0xcc}, + {0xbc, 0x0f, 0x05, 0xcc}, {0xbc, 0x10, 0xc0, 0xcc}, + {0xbc, 0x11, 0x03, 0xcc}, {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x05, 0xcc}, {0xb6, 0x02, 0x00, 0xcc}, + {0xb6, 0x05, 0x04, 0xcc}, {0xb6, 0x04, 0x00, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x29, 0xcc}, + {0xb6, 0x18, 0x0a, 0xcc}, {0xb6, 0x17, 0x00, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x26, 0xcc}, + {0xbf, 0xc1, 0x02, 0xcc}, {0xbf, 0xcc, 0x04, 0xcc}, + {0xbc, 0x02, 0x18, 0xcc}, {0xbc, 0x03, 0x50, 0xcc}, + {0xbc, 0x04, 0x18, 0xcc}, {0xbc, 0x05, 0x00, 0xcc}, + {0xbc, 0x06, 0x00, 0xcc}, {0xbc, 0x08, 0x30, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, {0xbc, 0x0a, 0x10, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, {0xbc, 0x0c, 0x00, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, {0xb3, 0x01, 0x41, 0xcc}, + {} +}; +static __u8 mi1320_initQVGA_data[][4] = { + {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, + {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, + {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, + {0xb3, 0x00, 0x64, 0xcc}, {0xb3, 0x00, 0x65, 0xcc}, + {0xb0, 0x16, 0x03, 0xcc}, {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x35, 0xc8, 0xcc}, {0xb3, 0x02, 0x00, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, {0xb3, 0x23, 0xe0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x00, 0x65, 0xcc}, {0xb8, 0x00, 0x00, 0xcc}, + {0xbc, 0x00, 0xd0, 0xcc}, {0xbc, 0x01, 0x01, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, {0x0d, 0x00, 0x09, 0xbb}, + {0x00, 0x01, 0x00, 0xdd}, {0x0d, 0x00, 0x08, 0xbb}, + {0xf0, 0x00, 0x00, 0xbb}, {0x02, 0x00, 0x64, 0xbb}, + {0x05, 0x01, 0x78, 0xbb}, {0x06, 0x00, 0x11, 0xbb}, + {0x07, 0x01, 0x42, 0xbb}, {0x08, 0x00, 0x11, 0xbb}, + {0x20, 0x01, 0x00, 0xbb}, {0x21, 0x80, 0x00, 0xbb}, + {0x22, 0x0d, 0x0f, 0xbb}, {0x24, 0x80, 0x00, 0xbb}, + {0x59, 0x00, 0xff, 0xbb}, {0xf0, 0x00, 0x01, 0xbb}, + {0x9d, 0x3c, 0xa0, 0xbb}, {0x47, 0x30, 0x30, 0xbb}, + {0xf0, 0x00, 0x00, 0xbb}, {0x0a, 0x80, 0x11, 0xbb}, + {0x35, 0x00, 0x22, 0xbb}, {0xf0, 0x00, 0x02, 0xbb}, + {0x9d, 0xc5, 0x05, 0xbb}, {0xdc, 0x0f, 0xfc, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, {0x06, 0x74, 0x0e, 0xbb}, + {0x80, 0x00, 0x06, 0xbb}, {0x81, 0x04, 0x00, 0xbb}, + {0x82, 0x01, 0x02, 0xbb}, {0x83, 0x03, 0x02, 0xbb}, + {0x84, 0x05, 0x00, 0xbb}, {0x85, 0x01, 0x00, 0xbb}, + {0x86, 0x03, 0x02, 0xbb}, {0x87, 0x05, 0x00, 0xbb}, + {0x88, 0x01, 0x00, 0xbb}, {0x89, 0x02, 0x02, 0xbb}, + {0x8a, 0xfd, 0x04, 0xbb}, {0x8b, 0xfc, 0xfd, 0xbb}, + {0x8c, 0xff, 0xfd, 0xbb}, {0x8d, 0x00, 0x00, 0xbb}, + {0x8e, 0xfe, 0x05, 0xbb}, {0x8f, 0xfc, 0xfd, 0xbb}, + {0x90, 0xfe, 0xfd, 0xbb}, {0x91, 0x00, 0x00, 0xbb}, + {0x92, 0xfe, 0x03, 0xbb}, {0x93, 0xfd, 0xfe, 0xbb}, + {0x94, 0xff, 0xfd, 0xbb}, {0x95, 0x00, 0x00, 0xbb}, + {0xb6, 0x07, 0x05, 0xbb}, {0xb7, 0x13, 0x06, 0xbb}, + {0xb8, 0x08, 0x06, 0xbb}, {0xb9, 0x14, 0x08, 0xbb}, + {0xba, 0x06, 0x05, 0xbb}, {0xbb, 0x13, 0x06, 0xbb}, + {0xbc, 0x03, 0x01, 0xbb}, {0xbd, 0x03, 0x04, 0xbb}, + {0xbe, 0x00, 0x02, 0xbb}, {0xbf, 0x03, 0x01, 0xbb}, + {0xc0, 0x02, 0x04, 0xbb}, {0xc1, 0x00, 0x04, 0xbb}, + {0xc2, 0x02, 0x01, 0xbb}, {0xc3, 0x01, 0x03, 0xbb}, + {0xc4, 0x00, 0x04, 0xbb}, {0xf0, 0x00, 0x02, 0xbb}, + {0xc8, 0x00, 0x00, 0xbb}, {0x2e, 0x00, 0x00, 0xbb}, + {0x2e, 0x0c, 0x5b, 0xbb}, {0x2f, 0xd1, 0x00, 0xbb}, + {0x39, 0x03, 0xca, 0xbb}, {0x3a, 0x06, 0x80, 0xbb}, + {0x3b, 0x01, 0x52, 0xbb}, {0x3c, 0x05, 0x40, 0xbb}, + {0x57, 0x01, 0x9c, 0xbb}, {0x58, 0x01, 0xee, 0xbb}, + {0x59, 0x00, 0xf0, 0xbb}, {0x5a, 0x01, 0x20, 0xbb}, + {0x5c, 0x1d, 0x17, 0xbb}, {0x5d, 0x22, 0x1c, 0xbb}, + {0x64, 0x1e, 0x1c, 0xbb}, {0x5b, 0x00, 0x01, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x36, 0x68, 0x10, 0xbb}, + {0x00, 0x00, 0x30, 0xdd}, {0x37, 0x81, 0x00, 0xbb}, + {0xbc, 0x02, 0x18, 0xcc}, {0xbc, 0x03, 0x50, 0xcc}, + {0xbc, 0x04, 0x18, 0xcc}, {0xbc, 0x05, 0x00, 0xcc}, + {0xbc, 0x06, 0x00, 0xcc}, {0xbc, 0x08, 0x30, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, {0xbc, 0x0a, 0x10, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, {0xbc, 0x0c, 0x00, 0xcc}, + {0xbf, 0xc0, 0x26, 0xcc}, {0xbf, 0xc1, 0x02, 0xcc}, + {0xbf, 0xcc, 0x04, 0xcc}, {0xb3, 0x5c, 0x01, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {} +}; + +static __u8 po3130_gamma[17] = { + 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, + 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff +}; +static __u8 po3130_matrix[9] = { + 0x5f, 0xec, 0xf5, 0xf1, 0x5a, 0xf5, 0xf1, 0xec, 0x63 +}; + +static __u8 po3130_initVGA_data[][4] = { + {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, + {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, + {0xb3, 0x00, 0x04, 0xcc}, {0xb3, 0x00, 0x24, 0xcc}, + {0xb3, 0x00, 0x25, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, {0xb3, 0x03, 0x1a, 0xcc}, + {0xb3, 0x04, 0x15, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe8, 0xcc}, {0xb8, 0x08, 0xe8, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x34, 0x01, 0xcc}, {0xb3, 0x35, 0xf6, 0xcc}, + {0xb3, 0x00, 0x27, 0xcc}, {0xbc, 0x00, 0x71, 0xcc}, + {0xb8, 0x00, 0x21, 0xcc}, {0xb8, 0x27, 0x20, 0xcc}, + {0xb8, 0x01, 0x79, 0xcc}, {0xb8, 0x81, 0x09, 0xcc}, + {0xb8, 0x2c, 0x50, 0xcc}, {0xb8, 0x2d, 0xf8, 0xcc}, + {0xb8, 0x2e, 0xf8, 0xcc}, {0xb8, 0x2f, 0xf8, 0xcc}, + {0xb8, 0x30, 0x50, 0xcc}, {0xb8, 0x31, 0xf8, 0xcc}, + {0xb8, 0x32, 0xf8, 0xcc}, {0xb8, 0x33, 0xf8, 0xcc}, + {0xb8, 0x34, 0x50, 0xcc}, {0xb8, 0x35, 0x00, 0xcc}, + {0xb8, 0x36, 0x00, 0xcc}, {0xb8, 0x37, 0x00, 0xcc}, + {0x00, 0x1e, 0xc6, 0xaa}, {0x00, 0x20, 0x44, 0xaa}, + {0x00, 0xad, 0x02, 0xaa}, {0x00, 0xae, 0x2c, 0xaa}, + {0x00, 0x12, 0x08, 0xaa}, {0x00, 0x17, 0x41, 0xaa}, + {0x00, 0x19, 0x41, 0xaa}, {0x00, 0x1e, 0x06, 0xaa}, + {0x00, 0x21, 0x00, 0xaa}, {0x00, 0x36, 0xc0, 0xaa}, + {0x00, 0x37, 0xc8, 0xaa}, {0x00, 0x3b, 0x36, 0xaa}, + {0x00, 0x4b, 0xfe, 0xaa}, {0x00, 0x51, 0x1c, 0xaa}, + {0x00, 0x52, 0x01, 0xaa}, {0x00, 0x55, 0x0a, 0xaa}, + {0x00, 0x59, 0x02, 0xaa}, {0x00, 0x5a, 0x04, 0xaa}, + {0x00, 0x5c, 0x10, 0xaa}, {0x00, 0x5d, 0x10, 0xaa}, + {0x00, 0x5e, 0x10, 0xaa}, {0x00, 0x5f, 0x10, 0xaa}, + {0x00, 0x61, 0x00, 0xaa}, {0x00, 0x62, 0x18, 0xaa}, + {0x00, 0x63, 0x30, 0xaa}, {0x00, 0x70, 0x68, 0xaa}, + {0x00, 0x80, 0x71, 0xaa}, {0x00, 0x81, 0x08, 0xaa}, + {0x00, 0x82, 0x00, 0xaa}, {0x00, 0x83, 0x55, 0xaa}, + {0x00, 0x84, 0x06, 0xaa}, {0x00, 0x85, 0x06, 0xaa}, + {0x00, 0x86, 0x13, 0xaa}, {0x00, 0x87, 0x18, 0xaa}, + {0x00, 0xaa, 0x3f, 0xaa}, {0x00, 0xab, 0x44, 0xaa}, + {0x00, 0xb0, 0x68, 0xaa}, {0x00, 0xb5, 0x10, 0xaa}, + {0x00, 0xb8, 0x20, 0xaa}, {0x00, 0xb9, 0xa0, 0xaa}, + {0x00, 0xbc, 0x04, 0xaa}, {0x00, 0x8b, 0x40, 0xaa}, + {0x00, 0x8c, 0x91, 0xaa}, {0x00, 0x8d, 0x8f, 0xaa}, + {0x00, 0x8e, 0x91, 0xaa}, {0x00, 0x8f, 0x43, 0xaa}, + {0x00, 0x90, 0x92, 0xaa}, {0x00, 0x91, 0x89, 0xaa}, + {0x00, 0x92, 0x9d, 0xaa}, {0x00, 0x93, 0x46, 0xaa}, + {0x00, 0xd6, 0x22, 0xaa}, {0x00, 0x73, 0x00, 0xaa}, + {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa}, + {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa}, + {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa}, + {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa}, + {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa}, + {0x00, 0x7e, 0xea, 0xaa}, {0x00, 0xd6, 0x62, 0xaa}, + {0x00, 0x73, 0x00, 0xaa}, {0x00, 0x74, 0x10, 0xaa}, + {0x00, 0x75, 0x20, 0xaa}, {0x00, 0x76, 0x2b, 0xaa}, + {0x00, 0x77, 0x36, 0xaa}, {0x00, 0x78, 0x49, 0xaa}, + {0x00, 0x79, 0x5a, 0xaa}, {0x00, 0x7a, 0x7f, 0xaa}, + {0x00, 0x7b, 0x9b, 0xaa}, {0x00, 0x7c, 0xba, 0xaa}, + {0x00, 0x7d, 0xd4, 0xaa}, {0x00, 0x7e, 0xea, 0xaa}, + {0x00, 0xd6, 0xa2, 0xaa}, {0x00, 0x73, 0x00, 0xaa}, + {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa}, + {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa}, + {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa}, + {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa}, + {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa}, + {0x00, 0x7e, 0xea, 0xaa}, + {0x00, 0x4c, 0x07, 0xaa}, + {0x00, 0x4b, 0xe0, 0xaa}, {0x00, 0x4e, 0x77, 0xaa}, + {0x00, 0x59, 0x02, 0xaa}, {0x00, 0x4d, 0x0a, 0xaa}, +/* {0x00, 0xd1, 0x00, 0xaa}, {0x00, 0x20, 0xc4, 0xaa}, + {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc}, */ + {0x00, 0xd1, 0x3c, 0xaa}, {0x00, 0x20, 0xc4, 0xaa}, + {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc}, + {0xb8, 0xfe, 0x00, 0xcc}, {0xb8, 0xff, 0x28, 0xcc}, + {0xb9, 0x00, 0x28, 0xcc}, {0xb9, 0x01, 0x28, 0xcc}, + {0xb9, 0x02, 0x28, 0xcc}, {0xb9, 0x03, 0x00, 0xcc}, + {0xb9, 0x04, 0x00, 0xcc}, {0xb9, 0x05, 0x3c, 0xcc}, + {0xb9, 0x06, 0x3c, 0xcc}, {0xb9, 0x07, 0x3c, 0xcc}, + {0xb9, 0x08, 0x3c, 0xcc}, {0x00, 0x05, 0x00, 0xaa}, + {0xb3, 0x5c, 0x00, 0xcc}, {0xb3, 0x01, 0x41, 0xcc}, + {} +}; +static __u8 po3130_rundata[][4] = { + {0x00, 0x47, 0x45, 0xaa}, {0x00, 0x48, 0x9b, 0xaa}, + {0x00, 0x49, 0x3a, 0xaa}, {0x00, 0x4a, 0x01, 0xaa}, + {0x00, 0x44, 0x40, 0xaa}, +/* {0x00, 0xd5, 0x7c, 0xaa}, */ + {0x00, 0xad, 0x04, 0xaa}, {0x00, 0xae, 0x00, 0xaa}, + {0x00, 0xb0, 0x78, 0xaa}, {0x00, 0x98, 0x02, 0xaa}, + {0x00, 0x94, 0x25, 0xaa}, {0x00, 0x95, 0x25, 0xaa}, + {0x00, 0x59, 0x68, 0xaa}, {0x00, 0x44, 0x20, 0xaa}, + {0x00, 0x17, 0x50, 0xaa}, {0x00, 0x19, 0x50, 0xaa}, + {0x00, 0xd1, 0x3c, 0xaa}, {0x00, 0xd1, 0x3c, 0xaa}, + {0x00, 0x1e, 0x06, 0xaa}, {0x00, 0x1e, 0x06, 0xaa}, + {} +}; + +static __u8 po3130_initQVGA_data[][4] = { + {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, + {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x09, 0xcc}, + {0xb3, 0x00, 0x04, 0xcc}, {0xb3, 0x00, 0x24, 0xcc}, + {0xb3, 0x00, 0x25, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, {0xb3, 0x03, 0x1a, 0xcc}, + {0xb3, 0x04, 0x15, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, {0xb8, 0x08, 0xe0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x34, 0x01, 0xcc}, {0xb3, 0x35, 0xf6, 0xcc}, + {0xb3, 0x00, 0x27, 0xcc}, {0xbc, 0x00, 0xd1, 0xcc}, + {0xb8, 0x00, 0x21, 0xcc}, {0xb8, 0x27, 0x20, 0xcc}, + {0xb8, 0x01, 0x79, 0xcc}, {0xb8, 0x81, 0x09, 0xcc}, + {0xb8, 0x2c, 0x50, 0xcc}, {0xb8, 0x2d, 0xf8, 0xcc}, + {0xb8, 0x2e, 0xf8, 0xcc}, {0xb8, 0x2f, 0xf8, 0xcc}, + {0xb8, 0x30, 0x50, 0xcc}, {0xb8, 0x31, 0xf8, 0xcc}, + {0xb8, 0x32, 0xf8, 0xcc}, {0xb8, 0x33, 0xf8, 0xcc}, + {0xb8, 0x34, 0x50, 0xcc}, {0xb8, 0x35, 0x00, 0xcc}, + {0xb8, 0x36, 0x00, 0xcc}, {0xb8, 0x37, 0x00, 0xcc}, + {0x00, 0x1e, 0xc6, 0xaa}, {0x00, 0x20, 0x44, 0xaa}, + {0x00, 0xad, 0x02, 0xaa}, {0x00, 0xae, 0x2c, 0xaa}, + {0x00, 0x12, 0x08, 0xaa}, {0x00, 0x17, 0x41, 0xaa}, + {0x00, 0x19, 0x41, 0xaa}, {0x00, 0x1e, 0x06, 0xaa}, + {0x00, 0x21, 0x00, 0xaa}, {0x00, 0x36, 0xc0, 0xaa}, + {0x00, 0x37, 0xc8, 0xaa}, {0x00, 0x3b, 0x36, 0xaa}, + {0x00, 0x4b, 0xfe, 0xaa}, {0x00, 0x51, 0x1c, 0xaa}, + {0x00, 0x52, 0x01, 0xaa}, {0x00, 0x55, 0x0a, 0xaa}, + {0x00, 0x59, 0x6f, 0xaa}, {0x00, 0x5a, 0x04, 0xaa}, + {0x00, 0x5c, 0x10, 0xaa}, {0x00, 0x5d, 0x10, 0xaa}, + {0x00, 0x5e, 0x10, 0xaa}, {0x00, 0x5f, 0x10, 0xaa}, + {0x00, 0x61, 0x00, 0xaa}, {0x00, 0x62, 0x18, 0xaa}, + {0x00, 0x63, 0x30, 0xaa}, {0x00, 0x70, 0x68, 0xaa}, + {0x00, 0x80, 0x71, 0xaa}, {0x00, 0x81, 0x08, 0xaa}, + {0x00, 0x82, 0x00, 0xaa}, {0x00, 0x83, 0x55, 0xaa}, + {0x00, 0x84, 0x06, 0xaa}, {0x00, 0x85, 0x06, 0xaa}, + {0x00, 0x86, 0x13, 0xaa}, {0x00, 0x87, 0x18, 0xaa}, + {0x00, 0xaa, 0x3f, 0xaa}, {0x00, 0xab, 0x44, 0xaa}, + {0x00, 0xb0, 0x68, 0xaa}, {0x00, 0xb5, 0x10, 0xaa}, + {0x00, 0xb8, 0x20, 0xaa}, {0x00, 0xb9, 0xa0, 0xaa}, + {0x00, 0xbc, 0x04, 0xaa}, {0x00, 0x8b, 0x40, 0xaa}, + {0x00, 0x8c, 0x91, 0xaa}, {0x00, 0x8d, 0x8f, 0xaa}, + {0x00, 0x8e, 0x91, 0xaa}, {0x00, 0x8f, 0x43, 0xaa}, + {0x00, 0x90, 0x92, 0xaa}, {0x00, 0x91, 0x89, 0xaa}, + {0x00, 0x92, 0x9d, 0xaa}, {0x00, 0x93, 0x46, 0xaa}, + {0x00, 0xd6, 0x22, 0xaa}, {0x00, 0x73, 0x00, 0xaa}, + {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa}, + {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa}, + {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa}, + {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa}, + {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa}, + {0x00, 0x7e, 0xea, 0xaa}, {0x00, 0xd6, 0x62, 0xaa}, + {0x00, 0x73, 0x00, 0xaa}, {0x00, 0x74, 0x10, 0xaa}, + {0x00, 0x75, 0x20, 0xaa}, {0x00, 0x76, 0x2b, 0xaa}, + {0x00, 0x77, 0x36, 0xaa}, {0x00, 0x78, 0x49, 0xaa}, + {0x00, 0x79, 0x5a, 0xaa}, {0x00, 0x7a, 0x7f, 0xaa}, + {0x00, 0x7b, 0x9b, 0xaa}, {0x00, 0x7c, 0xba, 0xaa}, + {0x00, 0x7d, 0xd4, 0xaa}, {0x00, 0x7e, 0xea, 0xaa}, + {0x00, 0xd6, 0xa2, 0xaa}, {0x00, 0x73, 0x00, 0xaa}, + {0x00, 0x74, 0x10, 0xaa}, {0x00, 0x75, 0x20, 0xaa}, + {0x00, 0x76, 0x2b, 0xaa}, {0x00, 0x77, 0x36, 0xaa}, + {0x00, 0x78, 0x49, 0xaa}, {0x00, 0x79, 0x5a, 0xaa}, + {0x00, 0x7a, 0x7f, 0xaa}, {0x00, 0x7b, 0x9b, 0xaa}, + {0x00, 0x7c, 0xba, 0xaa}, {0x00, 0x7d, 0xd4, 0xaa}, + {0x00, 0x7e, 0xea, 0xaa}, {0x00, 0x4c, 0x07, 0xaa}, + {0x00, 0x4b, 0xe0, 0xaa}, {0x00, 0x4e, 0x77, 0xaa}, + {0x00, 0x59, 0x66, 0xaa}, {0x00, 0x4d, 0x0a, 0xaa}, + {0x00, 0xd1, 0x00, 0xaa}, {0x00, 0x20, 0xc4, 0xaa}, + {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc}, + {0xb8, 0xfe, 0x00, 0xcc}, {0xb8, 0xff, 0x28, 0xcc}, + {0xb9, 0x00, 0x28, 0xcc}, {0xb9, 0x01, 0x28, 0xcc}, + {0xb9, 0x02, 0x28, 0xcc}, {0xb9, 0x03, 0x00, 0xcc}, + {0xb9, 0x04, 0x00, 0xcc}, {0xb9, 0x05, 0x3c, 0xcc}, + {0xb9, 0x06, 0x3c, 0xcc}, {0xb9, 0x07, 0x3c, 0xcc}, + {0xb9, 0x08, 0x3c, 0xcc}, {0xbc, 0x02, 0x18, 0xcc}, + {0xbc, 0x03, 0x50, 0xcc}, {0xbc, 0x04, 0x18, 0xcc}, + {0xbc, 0x05, 0x00, 0xcc}, {0xbc, 0x06, 0x00, 0xcc}, + {0xbc, 0x08, 0x30, 0xcc}, {0xbc, 0x09, 0x40, 0xcc}, + {0xbc, 0x0a, 0x10, 0xcc}, {0xbc, 0x0b, 0x00, 0xcc}, + {0xbc, 0x0c, 0x00, 0xcc}, {0x00, 0x05, 0x00, 0xaa}, + {0xb3, 0x5c, 0x00, 0xcc}, {0xb3, 0x01, 0x41, 0xcc}, + {} +}; + +static __u8 hv7131r_gamma[17] = { +/* 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, + * 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff */ + 0x04, 0x1a, 0x36, 0x55, 0x6f, 0x87, 0x9d, 0xb0, 0xc1, + 0xcf, 0xda, 0xe4, 0xec, 0xf3, 0xf8, 0xfd, 0xff +}; +static __u8 hv7131r_matrix[9] = { + 0x5f, 0xec, 0xf5, 0xf1, 0x5a, 0xf5, 0xf1, 0xec, 0x63 +}; +static __u8 hv7131r_initVGA_data[][4] = { + {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, + {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, + {0xb3, 0x00, 0x24, 0xcc}, + {0xb3, 0x00, 0x25, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x01, 0x45, 0xcc}, {0xb3, 0x03, 0x0b, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, {0xb3, 0x23, 0xe0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, + {0xb3, 0x17, 0x7f, 0xcc}, {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x35, 0x91, 0xcc}, {0xb3, 0x00, 0x27, 0xcc}, + {0xbc, 0x00, 0x73, 0xcc}, + {0xb8, 0x00, 0x23, 0xcc}, {0x00, 0x01, 0x0c, 0xaa}, + {0x00, 0x14, 0x01, 0xaa}, {0x00, 0x15, 0xe6, 0xaa}, + {0x00, 0x16, 0x02, 0xaa}, + {0x00, 0x17, 0x86, 0xaa}, {0x00, 0x23, 0x00, 0xaa}, + {0x00, 0x25, 0x09, 0xaa}, {0x00, 0x26, 0x27, 0xaa}, + {0x00, 0x27, 0xc0, 0xaa}, + {0xb8, 0x2c, 0x60, 0xcc}, {0xb8, 0x2d, 0xf8, 0xcc}, + {0xb8, 0x2e, 0xf8, 0xcc}, {0xb8, 0x2f, 0xf8, 0xcc}, + {0xb8, 0x30, 0x50, 0xcc}, + {0xb8, 0x31, 0xf8, 0xcc}, {0xb8, 0x32, 0xf8, 0xcc}, + {0xb8, 0x33, 0xf8, 0xcc}, {0xb8, 0x34, 0x65, 0xcc}, + {0xb8, 0x35, 0x00, 0xcc}, + {0xb8, 0x36, 0x00, 0xcc}, {0xb8, 0x37, 0x00, 0xcc}, + {0xb8, 0x27, 0x20, 0xcc}, {0xb8, 0x01, 0x7d, 0xcc}, + {0xb8, 0x81, 0x09, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, {0xb8, 0xfe, 0x00, 0xcc}, + {0xb8, 0xff, 0x28, 0xcc}, {0xb9, 0x00, 0x28, 0xcc}, + {0xb9, 0x01, 0x28, 0xcc}, + {0xb9, 0x02, 0x28, 0xcc}, {0xb9, 0x03, 0x00, 0xcc}, + {0xb9, 0x04, 0x00, 0xcc}, {0xb9, 0x05, 0x3c, 0xcc}, + {0xb9, 0x06, 0x3c, 0xcc}, + {0xb9, 0x07, 0x3c, 0xcc}, {0xb9, 0x08, 0x3c, 0xcc}, + {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc}, + {0x00, 0x30, 0x18, 0xaa}, + {} +}; + +static __u8 hv7131r_initQVGA_data[][4] = { + {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, + {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, + {0xb3, 0x00, 0x24, 0xcc}, + {0xb3, 0x00, 0x25, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x03, 0x0b, 0xcc}, {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, {0xb3, 0x16, 0x02, 0xcc}, + {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x34, 0x01, 0xcc}, {0xb3, 0x35, 0x91, 0xcc}, + {0xb3, 0x00, 0x27, 0xcc}, {0xbc, 0x00, 0xd1, 0xcc}, + {0xb8, 0x00, 0x21, 0xcc}, + {0x00, 0x01, 0x0c, 0xaa}, {0x00, 0x14, 0x01, 0xaa}, + {0x00, 0x15, 0xe6, 0xaa}, {0x00, 0x16, 0x02, 0xaa}, + {0x00, 0x17, 0x86, 0xaa}, + {0x00, 0x23, 0x00, 0xaa}, {0x00, 0x25, 0x01, 0xaa}, + {0x00, 0x26, 0xd4, 0xaa}, {0x00, 0x27, 0xc0, 0xaa}, + {0xbc, 0x02, 0x08, 0xcc}, + {0xbc, 0x03, 0x70, 0xcc}, {0xbc, 0x04, 0x08, 0xcc}, + {0xbc, 0x05, 0x00, 0xcc}, {0xbc, 0x06, 0x00, 0xcc}, + {0xbc, 0x08, 0x3c, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, {0xbc, 0x0a, 0x04, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, {0xbc, 0x0c, 0x00, 0xcc}, + {0xb8, 0xfe, 0x02, 0xcc}, + {0xb8, 0xff, 0x07, 0xcc}, {0xb9, 0x00, 0x14, 0xcc}, + {0xb9, 0x01, 0x14, 0xcc}, {0xb9, 0x02, 0x14, 0xcc}, + {0xb9, 0x03, 0x00, 0xcc}, + {0xb9, 0x04, 0x02, 0xcc}, {0xb9, 0x05, 0x05, 0xcc}, + {0xb9, 0x06, 0x0f, 0xcc}, {0xb9, 0x07, 0x0f, 0xcc}, + {0xb9, 0x08, 0x0f, 0xcc}, + {0xb8, 0x2c, 0x60, 0xcc}, {0xb8, 0x2d, 0xf8, 0xcc}, + {0xb8, 0x2e, 0xf8, 0xcc}, {0xb8, 0x2f, 0xf8, 0xcc}, + {0xb8, 0x30, 0x50, 0xcc}, + {0xb8, 0x31, 0xf8, 0xcc}, {0xb8, 0x32, 0xf8, 0xcc}, + {0xb8, 0x33, 0xf8, 0xcc}, + {0xb8, 0x34, 0x65, 0xcc}, {0xb8, 0x35, 0x00, 0xcc}, + {0xb8, 0x36, 0x00, 0xcc}, {0xb8, 0x37, 0x00, 0xcc}, + {0xb8, 0x27, 0x20, 0xcc}, + {0xb8, 0x01, 0x7d, 0xcc}, {0xb8, 0x81, 0x09, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, {0xb8, 0xfe, 0x00, 0xcc}, + {0xb8, 0xff, 0x28, 0xcc}, + {0xb9, 0x00, 0x28, 0xcc}, {0xb9, 0x01, 0x28, 0xcc}, + {0xb9, 0x02, 0x28, 0xcc}, {0xb9, 0x03, 0x00, 0xcc}, + {0xb9, 0x04, 0x00, 0xcc}, + {0xb9, 0x05, 0x3c, 0xcc}, {0xb9, 0x06, 0x3c, 0xcc}, + {0xb9, 0x07, 0x3c, 0xcc}, {0xb9, 0x08, 0x3c, 0xcc}, + {0xb8, 0x8e, 0x00, 0xcc}, + {0xb8, 0x8f, 0xff, 0xcc}, {0x00, 0x30, 0x18, 0xaa}, + {} +}; + +static __u8 ov7660_gamma[17] = { + 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, + 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff +}; +static __u8 ov7660_matrix[9] = { + 0x5a, 0xf0, 0xf6, 0xf3, 0x57, 0xf6, 0xf3, 0xef, 0x62 +}; +static __u8 ov7660_initVGA_data[][4] = { + {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, + {0x00, 0x00, 0x50, 0xdd}, + {0xb0, 0x03, 0x01, 0xcc}, + {0xb3, 0x00, 0x21, 0xcc}, {0xb3, 0x00, 0x26, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x03, 0xcc}, + {0xb3, 0x03, 0x1f, 0xcc}, {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc},/* 0xb315 <-0 href startl */ + {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, {0xb3, 0x1d, 0x01, 0xcc}, + {0xb3, 0x1f, 0x02, 0xcc}, + {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x35, 0xa1, 0xcc}, {0xb3, 0x00, 0x26, 0xcc}, + {0xb8, 0x00, 0x33, 0xcc}, /* 13 */ + {0xb8, 0x01, 0x7d, 0xcc}, + {0xbc, 0x00, 0x73, 0xcc}, {0xb8, 0x81, 0x09, 0xcc}, + {0xb8, 0x27, 0x20, 0xcc}, + {0xb8, 0x8f, 0x50, 0xcc}, + {0x00, 0x01, 0x80, 0xaa}, {0x00, 0x02, 0x80, 0xaa}, + {0x00, 0x12, 0x80, 0xaa}, + {0x00, 0x12, 0x05, 0xaa}, + {0x00, 0x1e, 0x01, 0xaa}, + {0x00, 0x3d, 0x40, 0xaa}, /* 0x3d <-40 gamma 01 */ + {0x00, 0x41, 0x00, 0xaa}, /* edge 00 */ + {0x00, 0x0d, 0x48, 0xaa}, {0x00, 0x0e, 0x04, 0xaa}, + {0x00, 0x13, 0xa7, 0xaa}, + {0x00, 0x40, 0xc1, 0xaa}, {0x00, 0x35, 0x00, 0xaa}, + {0x00, 0x36, 0x00, 0xaa}, + {0x00, 0x3c, 0x68, 0xaa}, {0x00, 0x1b, 0x05, 0xaa}, + {0x00, 0x39, 0x43, 0xaa}, + {0x00, 0x8d, 0xcf, 0xaa}, + {0x00, 0x8b, 0xcc, 0xaa}, {0x00, 0x8c, 0xcc, 0xaa}, + {0x00, 0x0f, 0x62, 0xaa}, + {0x00, 0x35, 0x84, 0xaa}, + {0x00, 0x3b, 0x08, 0xaa}, /* 0 * Nightframe 1/4 + 50Hz -> 0xC8 */ + {0x00, 0x3a, 0x00, 0xaa}, /* mx change yuyv format 00, 04, 01; 08, 0c*/ + {0x00, 0x14, 0x2a, 0xaa}, /* agc ampli */ + {0x00, 0x9e, 0x40, 0xaa}, {0xb8, 0x8f, 0x50, 0xcc}, + {0x00, 0x01, 0x80, 0xaa}, + {0x00, 0x02, 0x80, 0xaa}, + {0xb8, 0xfe, 0x00, 0xcc}, {0xb8, 0xff, 0x28, 0xcc}, + {0xb9, 0x00, 0x28, 0xcc}, + {0xb9, 0x01, 0x28, 0xcc}, {0xb9, 0x02, 0x28, 0xcc}, + {0xb9, 0x03, 0x00, 0xcc}, + {0xb9, 0x04, 0x00, 0xcc}, + {0xb9, 0x05, 0x3c, 0xcc}, {0xb9, 0x06, 0x3c, 0xcc}, + {0xb9, 0x07, 0x3c, 0xcc}, + {0xb9, 0x08, 0x3c, 0xcc}, + + {0xb8, 0x8e, 0x00, 0xcc}, {0xb8, 0x8f, 0xff, 0xcc}, + + {0x00, 0x29, 0x3c, 0xaa}, {0xb3, 0x01, 0x45, 0xcc}, + {} +}; +static __u8 ov7660_initQVGA_data[][4] = { + {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, + {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, + {0xb3, 0x00, 0x21, 0xcc}, {0xb3, 0x00, 0x26, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, {0xb3, 0x06, 0x03, 0xcc}, + {0xb3, 0x03, 0x1f, 0xcc}, {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc},/* 0xb315 <-0 href startl */ + {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, {0xb3, 0x1d, 0x01, 0xcc}, + {0xb3, 0x1f, 0x02, 0xcc}, {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x35, 0xa1, 0xcc}, {0xb3, 0x00, 0x26, 0xcc}, + {0xb8, 0x00, 0x33, 0xcc}, /* 13 */ + {0xb8, 0x01, 0x7d, 0xcc}, +/* sizer */ + {0xbc, 0x00, 0xd3, 0xcc}, + {0xb8, 0x81, 0x09, 0xcc}, {0xb8, 0x81, 0x09, 0xcc}, + {0xb8, 0x27, 0x20, 0xcc}, {0xb8, 0x8f, 0x50, 0xcc}, + {0x00, 0x01, 0x80, 0xaa}, {0x00, 0x02, 0x80, 0xaa}, + {0x00, 0x12, 0x80, 0xaa}, {0x00, 0x12, 0x05, 0xaa}, + {0x00, 0x1e, 0x01, 0xaa}, + {0x00, 0x3d, 0x40, 0xaa}, /* 0x3d <-40 gamma 01 */ + {0x00, 0x41, 0x00, 0xaa}, /* edge 00 */ + {0x00, 0x0d, 0x48, 0xaa}, {0x00, 0x0e, 0x04, 0xaa}, + {0x00, 0x13, 0xa7, 0xaa}, + {0x00, 0x40, 0xc1, 0xaa}, {0x00, 0x35, 0x00, 0xaa}, + {0x00, 0x36, 0x00, 0xaa}, + {0x00, 0x3c, 0x68, 0xaa}, {0x00, 0x1b, 0x05, 0xaa}, + {0x00, 0x39, 0x43, 0xaa}, {0x00, 0x8d, 0xcf, 0xaa}, + {0x00, 0x8b, 0xcc, 0xaa}, {0x00, 0x8c, 0xcc, 0xaa}, + {0x00, 0x0f, 0x62, 0xaa}, {0x00, 0x35, 0x84, 0xaa}, + {0x00, 0x3b, 0x08, 0xaa}, /* 0 * Nightframe 1/4 + 50Hz -> 0xC8 */ + {0x00, 0x3a, 0x00, 0xaa}, /* mx change yuyv format 00, 04, 01; 08, 0c*/ + {0x00, 0x14, 0x2a, 0xaa}, /* agc ampli */ + {0x00, 0x9e, 0x40, 0xaa}, {0xb8, 0x8f, 0x50, 0xcc}, + {0x00, 0x01, 0x80, 0xaa}, + {0x00, 0x02, 0x80, 0xaa}, +/* sizer filters */ + {0xbc, 0x02, 0x08, 0xcc}, + {0xbc, 0x03, 0x70, 0xcc}, + {0xb8, 0x35, 0x00, 0xcc}, + {0xb8, 0x36, 0x00, 0xcc}, + {0xb8, 0x37, 0x00, 0xcc}, + {0xbc, 0x04, 0x08, 0xcc}, + {0xbc, 0x05, 0x00, 0xcc}, + {0xbc, 0x06, 0x00, 0xcc}, + {0xbc, 0x08, 0x3c, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, + {0xbc, 0x0a, 0x04, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, + {0xbc, 0x0c, 0x00, 0xcc}, +/* */ + {0xb8, 0xfe, 0x00, 0xcc}, + {0xb8, 0xff, 0x28, 0xcc}, +/* */ + {0xb9, 0x00, 0x28, 0xcc}, {0xb9, 0x01, 0x28, 0xcc}, + {0xb9, 0x02, 0x28, 0xcc}, {0xb9, 0x03, 0x00, 0xcc}, + {0xb9, 0x04, 0x00, 0xcc}, {0xb9, 0x05, 0x3c, 0xcc}, + {0xb9, 0x06, 0x3c, 0xcc}, {0xb9, 0x07, 0x3c, 0xcc}, + {0xb9, 0x08, 0x3c, 0xcc}, +/* */ + {0xb8, 0x8e, 0x00, 0xcc}, + {0xb8, 0x8f, 0xff, 0xcc}, /* ff */ + {0x00, 0x29, 0x3c, 0xaa}, + {0xb3, 0x01, 0x45, 0xcc}, /* 45 */ + {0x00, 0x00, 0x00, 0x00} +}; + +static __u8 ov7660_50HZ[][4] = { + {0x00, 0x3b, 0x08, 0xaa}, + {0x00, 0x9d, 0x40, 0xaa}, + {0x00, 0x13, 0xa7, 0xaa}, + {0x00, 0x00, 0x00, 0x00} +}; + +static __u8 ov7660_60HZ[][4] = { + {0x00, 0x3b, 0x00, 0xaa}, + {0x00, 0x9e, 0x40, 0xaa}, + {0x00, 0x13, 0xa7, 0xaa}, + {} +}; + +static __u8 ov7660_NoFliker[][4] = { + {0x00, 0x13, 0x87, 0xaa}, + {} +}; + +static __u8 ov7670_initVGA_JPG[][4] = { + {0xb3, 0x01, 0x05, 0xcc}, + {0x00, 0x00, 0x30, 0xdd}, {0xb0, 0x03, 0x19, 0xcc}, + {0x00, 0x00, 0x10, 0xdd}, + {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x10, 0xdd}, + {0xb3, 0x00, 0x66, 0xcc}, {0xb3, 0x00, 0x67, 0xcc}, + {0xb3, 0x35, 0xa1, 0xcc}, {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x02, 0x02, 0xcc}, {0xb3, 0x03, 0x1f, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, {0xbc, 0x00, 0x41, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, {0x00, 0x12, 0x80, 0xaa}, + {0x00, 0x00, 0x20, 0xdd}, {0x00, 0x12, 0x00, 0xaa}, + {0x00, 0x11, 0x40, 0xaa}, {0x00, 0x6b, 0x0a, 0xaa}, + {0x00, 0x3a, 0x04, 0xaa}, {0x00, 0x40, 0xc0, 0xaa}, + {0x00, 0x8c, 0x00, 0xaa}, {0x00, 0x7a, 0x29, 0xaa}, + {0x00, 0x7b, 0x0e, 0xaa}, {0x00, 0x7c, 0x1a, 0xaa}, + {0x00, 0x7d, 0x31, 0xaa}, {0x00, 0x7e, 0x53, 0xaa}, + {0x00, 0x7f, 0x60, 0xaa}, {0x00, 0x80, 0x6b, 0xaa}, + {0x00, 0x81, 0x73, 0xaa}, {0x00, 0x82, 0x7b, 0xaa}, + {0x00, 0x83, 0x82, 0xaa}, {0x00, 0x84, 0x89, 0xaa}, + {0x00, 0x85, 0x96, 0xaa}, {0x00, 0x86, 0xa1, 0xaa}, + {0x00, 0x87, 0xb7, 0xaa}, {0x00, 0x88, 0xcc, 0xaa}, + {0x00, 0x89, 0xe1, 0xaa}, {0x00, 0x13, 0xe0, 0xaa}, + {0x00, 0x00, 0x00, 0xaa}, {0x00, 0x10, 0x00, 0xaa}, + {0x00, 0x0d, 0x40, 0xaa}, {0x00, 0x14, 0x28, 0xaa}, + {0x00, 0xa5, 0x05, 0xaa}, {0x00, 0xab, 0x07, 0xaa}, + {0x00, 0x24, 0x95, 0xaa}, {0x00, 0x25, 0x33, 0xaa}, + {0x00, 0x26, 0xe3, 0xaa}, {0x00, 0x9f, 0x88, 0xaa}, + {0x00, 0xa0, 0x78, 0xaa}, {0x00, 0x55, 0x90, 0xaa}, + {0x00, 0xa1, 0x03, 0xaa}, {0x00, 0xa6, 0xe0, 0xaa}, + {0x00, 0xa7, 0xd8, 0xaa}, {0x00, 0xa8, 0xf0, 0xaa}, + {0x00, 0xa9, 0x90, 0xaa}, {0x00, 0xaa, 0x14, 0xaa}, + {0x00, 0x13, 0xe5, 0xaa}, {0x00, 0x0e, 0x61, 0xaa}, + {0x00, 0x0f, 0x4b, 0xaa}, {0x00, 0x16, 0x02, 0xaa}, + {0x00, 0x1e, 0x07, 0xaa}, {0x00, 0x21, 0x02, 0xaa}, + {0x00, 0x22, 0x91, 0xaa}, {0x00, 0x29, 0x07, 0xaa}, + {0x00, 0x33, 0x0b, 0xaa}, {0x00, 0x35, 0x0b, 0xaa}, + {0x00, 0x37, 0x1d, 0xaa}, {0x00, 0x38, 0x71, 0xaa}, + {0x00, 0x39, 0x2a, 0xaa}, {0x00, 0x3c, 0x78, 0xaa}, + {0x00, 0x4d, 0x40, 0xaa}, {0x00, 0x4e, 0x20, 0xaa}, + {0x00, 0x74, 0x19, 0xaa}, {0x00, 0x8d, 0x4f, 0xaa}, + {0x00, 0x8e, 0x00, 0xaa}, {0x00, 0x8f, 0x00, 0xaa}, + {0x00, 0x90, 0x00, 0xaa}, {0x00, 0x91, 0x00, 0xaa}, + {0x00, 0x96, 0x00, 0xaa}, {0x00, 0x9a, 0x80, 0xaa}, + {0x00, 0xb0, 0x84, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa}, + {0x00, 0xb2, 0x0e, 0xaa}, {0x00, 0xb3, 0x82, 0xaa}, + {0x00, 0xb8, 0x0a, 0xaa}, {0x00, 0x43, 0x14, 0xaa}, + {0x00, 0x44, 0xf0, 0xaa}, {0x00, 0x45, 0x45, 0xaa}, + {0x00, 0x46, 0x63, 0xaa}, {0x00, 0x47, 0x2d, 0xaa}, + {0x00, 0x48, 0x46, 0xaa}, {0x00, 0x59, 0x88, 0xaa}, + {0x00, 0x5a, 0xa0, 0xaa}, {0x00, 0x5b, 0xc6, 0xaa}, + {0x00, 0x5c, 0x7d, 0xaa}, {0x00, 0x5d, 0x5f, 0xaa}, + {0x00, 0x5e, 0x19, 0xaa}, {0x00, 0x6c, 0x0a, 0xaa}, + {0x00, 0x6d, 0x55, 0xaa}, {0x00, 0x6e, 0x11, 0xaa}, + {0x00, 0x6f, 0x9e, 0xaa}, {0x00, 0x69, 0x00, 0xaa}, + {0x00, 0x6a, 0x40, 0xaa}, {0x00, 0x01, 0x40, 0xaa}, + {0x00, 0x02, 0x40, 0xaa}, {0x00, 0x13, 0xe7, 0xaa}, + {0x00, 0x5f, 0xf0, 0xaa}, {0x00, 0x60, 0xf0, 0xaa}, + {0x00, 0x61, 0xf0, 0xaa}, {0x00, 0x27, 0xa0, 0xaa}, + {0x00, 0x28, 0x80, 0xaa}, {0x00, 0x2c, 0x90, 0xaa}, + {0x00, 0x4f, 0x66, 0xaa}, {0x00, 0x50, 0x66, 0xaa}, + {0x00, 0x51, 0x00, 0xaa}, {0x00, 0x52, 0x22, 0xaa}, + {0x00, 0x53, 0x5e, 0xaa}, {0x00, 0x54, 0x80, 0xaa}, + {0x00, 0x58, 0x9e, 0xaa}, {0x00, 0x41, 0x08, 0xaa}, + {0x00, 0x3f, 0x00, 0xaa}, {0x00, 0x75, 0x85, 0xaa}, + {0x00, 0x76, 0xe1, 0xaa}, {0x00, 0x4c, 0x00, 0xaa}, + {0x00, 0x77, 0x0a, 0xaa}, {0x00, 0x3d, 0x88, 0xaa}, + {0x00, 0x4b, 0x09, 0xaa}, {0x00, 0xc9, 0x60, 0xaa}, + {0x00, 0x41, 0x38, 0xaa}, {0x00, 0x62, 0x30, 0xaa}, + {0x00, 0x63, 0x30, 0xaa}, {0x00, 0x64, 0x08, 0xaa}, + {0x00, 0x94, 0x07, 0xaa}, {0x00, 0x95, 0x0b, 0xaa}, + {0x00, 0x65, 0x00, 0xaa}, {0x00, 0x66, 0x05, 0xaa}, + {0x00, 0x56, 0x50, 0xaa}, {0x00, 0x34, 0x11, 0xaa}, + {0x00, 0xa4, 0x88, 0xaa}, {0x00, 0x96, 0x00, 0xaa}, + {0x00, 0x97, 0x30, 0xaa}, {0x00, 0x98, 0x20, 0xaa}, + {0x00, 0x99, 0x30, 0xaa}, {0x00, 0x9a, 0x84, 0xaa}, + {0x00, 0x9b, 0x29, 0xaa}, {0x00, 0x9c, 0x03, 0xaa}, + {0x00, 0x78, 0x04, 0xaa}, {0x00, 0x79, 0x01, 0xaa}, + {0x00, 0xc8, 0xf0, 0xaa}, {0x00, 0x79, 0x0f, 0xaa}, + {0x00, 0xc8, 0x00, 0xaa}, {0x00, 0x79, 0x10, 0xaa}, + {0x00, 0xc8, 0x7e, 0xaa}, {0x00, 0x79, 0x0a, 0xaa}, + {0x00, 0xc8, 0x80, 0xaa}, {0x00, 0x79, 0x0b, 0xaa}, + {0x00, 0xc8, 0x01, 0xaa}, {0x00, 0x79, 0x0c, 0xaa}, + {0x00, 0xc8, 0x0f, 0xaa}, {0x00, 0x79, 0x0d, 0xaa}, + {0x00, 0xc8, 0x20, 0xaa}, {0x00, 0x79, 0x09, 0xaa}, + {0x00, 0xc8, 0x80, 0xaa}, {0x00, 0x79, 0x02, 0xaa}, + {0x00, 0xc8, 0xc0, 0xaa}, {0x00, 0x79, 0x03, 0xaa}, + {0x00, 0xc8, 0x40, 0xaa}, {0x00, 0x79, 0x05, 0xaa}, + {0x00, 0xc8, 0x30, 0xaa}, {0x00, 0x79, 0x26, 0xaa}, + {0x00, 0x11, 0x40, 0xaa}, {0x00, 0x3a, 0x04, 0xaa}, + {0x00, 0x12, 0x00, 0xaa}, {0x00, 0x40, 0xc0, 0xaa}, + {0x00, 0x8c, 0x00, 0xaa}, {0x00, 0x17, 0x14, 0xaa}, + {0x00, 0x18, 0x02, 0xaa}, {0x00, 0x32, 0x92, 0xaa}, + {0x00, 0x19, 0x02, 0xaa}, {0x00, 0x1a, 0x7a, 0xaa}, + {0x00, 0x03, 0x0a, 0xaa}, {0x00, 0x0c, 0x00, 0xaa}, + {0x00, 0x3e, 0x00, 0xaa}, {0x00, 0x70, 0x3a, 0xaa}, + {0x00, 0x71, 0x35, 0xaa}, {0x00, 0x72, 0x11, 0xaa}, + {0x00, 0x73, 0xf0, 0xaa}, {0x00, 0xa2, 0x02, 0xaa}, + {0x00, 0xb1, 0x00, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa}, + {0x00, 0x1e, 0x37, 0xaa}, {0x00, 0xaa, 0x14, 0xaa}, + {0x00, 0x24, 0x80, 0xaa}, {0x00, 0x25, 0x74, 0xaa}, + {0x00, 0x26, 0xd3, 0xaa}, {0x00, 0x0d, 0x00, 0xaa}, + {0x00, 0x14, 0x18, 0xaa}, {0x00, 0x9d, 0x99, 0xaa}, + {0x00, 0x9e, 0x7f, 0xaa}, {0x00, 0x64, 0x08, 0xaa}, + {0x00, 0x94, 0x07, 0xaa}, {0x00, 0x95, 0x06, 0xaa}, + {0x00, 0x66, 0x05, 0xaa}, {0x00, 0x41, 0x08, 0xaa}, + {0x00, 0x3f, 0x00, 0xaa}, {0x00, 0x75, 0x07, 0xaa}, + {0x00, 0x76, 0xe1, 0xaa}, {0x00, 0x4c, 0x00, 0xaa}, + {0x00, 0x77, 0x00, 0xaa}, {0x00, 0x3d, 0xc2, 0xaa}, + {0x00, 0x4b, 0x09, 0xaa}, {0x00, 0xc9, 0x60, 0xaa}, + {0x00, 0x41, 0x38, 0xaa}, {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x02, 0xcc}, {0xb6, 0x02, 0x80, 0xcc}, + {0xb6, 0x05, 0x01, 0xcc}, {0xb6, 0x04, 0xe0, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x13, 0xcc}, + {0xb6, 0x18, 0x02, 0xcc}, {0xb6, 0x17, 0x58, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, {0xbf, 0xcc, 0x00, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, {0xb3, 0x01, 0x45, 0xcc}, + {0x00, 0x77, 0x05, 0xaa}, + {}, +}; + +static __u8 ov7670_initQVGA_JPG[][4] = { + {0xb3, 0x01, 0x05, 0xcc}, {0x00, 0x00, 0x30, 0xdd}, + {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x10, 0xdd}, + {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x10, 0xdd}, + {0xb3, 0x00, 0x66, 0xcc}, {0xb3, 0x00, 0x67, 0xcc}, + {0xb3, 0x35, 0xa1, 0xcc}, {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x02, 0x02, 0xcc}, {0xb3, 0x03, 0x1f, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, {0xbc, 0x00, 0xd1, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, {0x00, 0x12, 0x80, 0xaa}, + {0x00, 0x00, 0x20, 0xdd}, {0x00, 0x12, 0x00, 0xaa}, + {0x00, 0x11, 0x40, 0xaa}, {0x00, 0x6b, 0x0a, 0xaa}, + {0x00, 0x3a, 0x04, 0xaa}, {0x00, 0x40, 0xc0, 0xaa}, + {0x00, 0x8c, 0x00, 0xaa}, {0x00, 0x7a, 0x29, 0xaa}, + {0x00, 0x7b, 0x0e, 0xaa}, {0x00, 0x7c, 0x1a, 0xaa}, + {0x00, 0x7d, 0x31, 0xaa}, {0x00, 0x7e, 0x53, 0xaa}, + {0x00, 0x7f, 0x60, 0xaa}, {0x00, 0x80, 0x6b, 0xaa}, + {0x00, 0x81, 0x73, 0xaa}, {0x00, 0x82, 0x7b, 0xaa}, + {0x00, 0x83, 0x82, 0xaa}, {0x00, 0x84, 0x89, 0xaa}, + {0x00, 0x85, 0x96, 0xaa}, {0x00, 0x86, 0xa1, 0xaa}, + {0x00, 0x87, 0xb7, 0xaa}, {0x00, 0x88, 0xcc, 0xaa}, + {0x00, 0x89, 0xe1, 0xaa}, {0x00, 0x13, 0xe0, 0xaa}, + {0x00, 0x00, 0x00, 0xaa}, {0x00, 0x10, 0x00, 0xaa}, + {0x00, 0x0d, 0x40, 0xaa}, {0x00, 0x14, 0x28, 0xaa}, + {0x00, 0xa5, 0x05, 0xaa}, {0x00, 0xab, 0x07, 0xaa}, + {0x00, 0x24, 0x95, 0xaa}, {0x00, 0x25, 0x33, 0xaa}, + {0x00, 0x26, 0xe3, 0xaa}, {0x00, 0x9f, 0x88, 0xaa}, + {0x00, 0xa0, 0x78, 0xaa}, {0x00, 0x55, 0x90, 0xaa}, + {0x00, 0xa1, 0x03, 0xaa}, {0x00, 0xa6, 0xe0, 0xaa}, + {0x00, 0xa7, 0xd8, 0xaa}, {0x00, 0xa8, 0xf0, 0xaa}, + {0x00, 0xa9, 0x90, 0xaa}, {0x00, 0xaa, 0x14, 0xaa}, + {0x00, 0x13, 0xe5, 0xaa}, {0x00, 0x0e, 0x61, 0xaa}, + {0x00, 0x0f, 0x4b, 0xaa}, {0x00, 0x16, 0x02, 0xaa}, + {0x00, 0x1e, 0x07, 0xaa}, {0x00, 0x21, 0x02, 0xaa}, + {0x00, 0x22, 0x91, 0xaa}, {0x00, 0x29, 0x07, 0xaa}, + {0x00, 0x33, 0x0b, 0xaa}, {0x00, 0x35, 0x0b, 0xaa}, + {0x00, 0x37, 0x1d, 0xaa}, {0x00, 0x38, 0x71, 0xaa}, + {0x00, 0x39, 0x2a, 0xaa}, {0x00, 0x3c, 0x78, 0xaa}, + {0x00, 0x4d, 0x40, 0xaa}, {0x00, 0x4e, 0x20, 0xaa}, + {0x00, 0x74, 0x19, 0xaa}, {0x00, 0x8d, 0x4f, 0xaa}, + {0x00, 0x8e, 0x00, 0xaa}, {0x00, 0x8f, 0x00, 0xaa}, + {0x00, 0x90, 0x00, 0xaa}, {0x00, 0x91, 0x00, 0xaa}, + {0x00, 0x96, 0x00, 0xaa}, {0x00, 0x9a, 0x80, 0xaa}, + {0x00, 0xb0, 0x84, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa}, + {0x00, 0xb2, 0x0e, 0xaa}, {0x00, 0xb3, 0x82, 0xaa}, + {0x00, 0xb8, 0x0a, 0xaa}, {0x00, 0x43, 0x14, 0xaa}, + {0x00, 0x44, 0xf0, 0xaa}, {0x00, 0x45, 0x45, 0xaa}, + {0x00, 0x46, 0x63, 0xaa}, {0x00, 0x47, 0x2d, 0xaa}, + {0x00, 0x48, 0x46, 0xaa}, {0x00, 0x59, 0x88, 0xaa}, + {0x00, 0x5a, 0xa0, 0xaa}, {0x00, 0x5b, 0xc6, 0xaa}, + {0x00, 0x5c, 0x7d, 0xaa}, {0x00, 0x5d, 0x5f, 0xaa}, + {0x00, 0x5e, 0x19, 0xaa}, {0x00, 0x6c, 0x0a, 0xaa}, + {0x00, 0x6d, 0x55, 0xaa}, {0x00, 0x6e, 0x11, 0xaa}, + {0x00, 0x6f, 0x9e, 0xaa}, {0x00, 0x69, 0x00, 0xaa}, + {0x00, 0x6a, 0x40, 0xaa}, {0x00, 0x01, 0x40, 0xaa}, + {0x00, 0x02, 0x40, 0xaa}, {0x00, 0x13, 0xe7, 0xaa}, + {0x00, 0x5f, 0xf0, 0xaa}, {0x00, 0x60, 0xf0, 0xaa}, + {0x00, 0x61, 0xf0, 0xaa}, {0x00, 0x27, 0xa0, 0xaa}, + {0x00, 0x28, 0x80, 0xaa}, {0x00, 0x2c, 0x90, 0xaa}, + {0x00, 0x4f, 0x66, 0xaa}, {0x00, 0x50, 0x66, 0xaa}, + {0x00, 0x51, 0x00, 0xaa}, {0x00, 0x52, 0x22, 0xaa}, + {0x00, 0x53, 0x5e, 0xaa}, {0x00, 0x54, 0x80, 0xaa}, + {0x00, 0x58, 0x9e, 0xaa}, {0x00, 0x41, 0x08, 0xaa}, + {0x00, 0x3f, 0x00, 0xaa}, {0x00, 0x75, 0x85, 0xaa}, + {0x00, 0x76, 0xe1, 0xaa}, {0x00, 0x4c, 0x00, 0xaa}, + {0x00, 0x77, 0x0a, 0xaa}, {0x00, 0x3d, 0x88, 0xaa}, + {0x00, 0x4b, 0x09, 0xaa}, {0x00, 0xc9, 0x60, 0xaa}, + {0x00, 0x41, 0x38, 0xaa}, {0x00, 0x62, 0x30, 0xaa}, + {0x00, 0x63, 0x30, 0xaa}, {0x00, 0x64, 0x08, 0xaa}, + {0x00, 0x94, 0x07, 0xaa}, {0x00, 0x95, 0x0b, 0xaa}, + {0x00, 0x65, 0x00, 0xaa}, {0x00, 0x66, 0x05, 0xaa}, + {0x00, 0x56, 0x50, 0xaa}, {0x00, 0x34, 0x11, 0xaa}, + {0x00, 0xa4, 0x88, 0xaa}, {0x00, 0x96, 0x00, 0xaa}, + {0x00, 0x97, 0x30, 0xaa}, {0x00, 0x98, 0x20, 0xaa}, + {0x00, 0x99, 0x30, 0xaa}, {0x00, 0x9a, 0x84, 0xaa}, + {0x00, 0x9b, 0x29, 0xaa}, {0x00, 0x9c, 0x03, 0xaa}, + {0x00, 0x78, 0x04, 0xaa}, {0x00, 0x79, 0x01, 0xaa}, + {0x00, 0xc8, 0xf0, 0xaa}, {0x00, 0x79, 0x0f, 0xaa}, + {0x00, 0xc8, 0x00, 0xaa}, {0x00, 0x79, 0x10, 0xaa}, + {0x00, 0xc8, 0x7e, 0xaa}, {0x00, 0x79, 0x0a, 0xaa}, + {0x00, 0xc8, 0x80, 0xaa}, {0x00, 0x79, 0x0b, 0xaa}, + {0x00, 0xc8, 0x01, 0xaa}, {0x00, 0x79, 0x0c, 0xaa}, + {0x00, 0xc8, 0x0f, 0xaa}, {0x00, 0x79, 0x0d, 0xaa}, + {0x00, 0xc8, 0x20, 0xaa}, {0x00, 0x79, 0x09, 0xaa}, + {0x00, 0xc8, 0x80, 0xaa}, {0x00, 0x79, 0x02, 0xaa}, + {0x00, 0xc8, 0xc0, 0xaa}, {0x00, 0x79, 0x03, 0xaa}, + {0x00, 0xc8, 0x40, 0xaa}, {0x00, 0x79, 0x05, 0xaa}, + {0x00, 0xc8, 0x30, 0xaa}, {0x00, 0x79, 0x26, 0xaa}, + {0x00, 0x11, 0x40, 0xaa}, {0x00, 0x3a, 0x04, 0xaa}, + {0x00, 0x12, 0x00, 0xaa}, {0x00, 0x40, 0xc0, 0xaa}, + {0x00, 0x8c, 0x00, 0xaa}, {0x00, 0x17, 0x14, 0xaa}, + {0x00, 0x18, 0x02, 0xaa}, {0x00, 0x32, 0x92, 0xaa}, + {0x00, 0x19, 0x02, 0xaa}, {0x00, 0x1a, 0x7a, 0xaa}, + {0x00, 0x03, 0x0a, 0xaa}, {0x00, 0x0c, 0x00, 0xaa}, + {0x00, 0x3e, 0x00, 0xaa}, {0x00, 0x70, 0x3a, 0xaa}, + {0x00, 0x71, 0x35, 0xaa}, {0x00, 0x72, 0x11, 0xaa}, + {0x00, 0x73, 0xf0, 0xaa}, {0x00, 0xa2, 0x02, 0xaa}, + {0x00, 0xb1, 0x00, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa}, + {0x00, 0x1e, 0x37, 0xaa}, {0x00, 0xaa, 0x14, 0xaa}, + {0x00, 0x24, 0x80, 0xaa}, {0x00, 0x25, 0x74, 0xaa}, + {0x00, 0x26, 0xd3, 0xaa}, {0x00, 0x0d, 0x00, 0xaa}, + {0x00, 0x14, 0x18, 0xaa}, {0x00, 0x9d, 0x99, 0xaa}, + {0x00, 0x9e, 0x7f, 0xaa}, {0x00, 0x64, 0x08, 0xaa}, + {0x00, 0x94, 0x07, 0xaa}, {0x00, 0x95, 0x06, 0xaa}, + {0x00, 0x66, 0x05, 0xaa}, {0x00, 0x41, 0x08, 0xaa}, + {0x00, 0x3f, 0x00, 0xaa}, {0x00, 0x75, 0x07, 0xaa}, + {0x00, 0x76, 0xe1, 0xaa}, {0x00, 0x4c, 0x00, 0xaa}, + {0x00, 0x77, 0x00, 0xaa}, {0x00, 0x3d, 0xc2, 0xaa}, + {0x00, 0x4b, 0x09, 0xaa}, {0x00, 0xc9, 0x60, 0xaa}, + {0x00, 0x41, 0x38, 0xaa}, {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x01, 0xcc}, {0xb6, 0x02, 0x40, 0xcc}, + {0xb6, 0x05, 0x00, 0xcc}, {0xb6, 0x04, 0xf0, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x21, 0xcc}, + {0xb6, 0x18, 0x00, 0xcc}, {0xb6, 0x17, 0x96, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, {0xbf, 0xcc, 0x00, 0xcc}, + {0xbc, 0x02, 0x18, 0xcc}, {0xbc, 0x03, 0x50, 0xcc}, + {0xbc, 0x04, 0x18, 0xcc}, {0xbc, 0x05, 0x00, 0xcc}, + {0xbc, 0x06, 0x00, 0xcc}, {0xbc, 0x08, 0x30, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, {0xbc, 0x0a, 0x10, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, {0xbc, 0x0c, 0x00, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, {0xb3, 0x01, 0x45, 0xcc}, + {0x00, 0x77, 0x05, 0xaa }, + {}, +}; + +struct sensor_info { + int sensorId; + __u8 I2cAdd; + __u8 IdAdd; + __u16 VpId; + __u8 m1; + __u8 m2; + __u8 op; + }; + +static struct sensor_info sensor_info_data[] = { +/* sensorId, I2cAdd, IdAdd, VpId, m1, m2, op */ + {SENSOR_HV7131R, 0x80 | 0x11, 0x00, 0x0209, 0x24, 0x25, 0x01}, + {SENSOR_OV7660, 0x80 | 0x21, 0x0a, 0x7660, 0x26, 0x26, 0x05}, + {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x3130, 0x24, 0x25, 0x01}, + {SENSOR_MI1320, 0x80 | 0xc8, 0x00, 0x148c, 0x64, 0x65, 0x01}, + {SENSOR_OV7670, 0x80 | 0x21, 0x0a, 0x7673, 0x66, 0x67, 0x05}, + {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01}, +}; + +static void reg_r(struct usb_device *dev, + __u16 req, + __u16 index, + __u8 *buffer, __u16 length) +{ + usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 1, /* value */ + index, buffer, length, + 500); +} + +static void reg_w(struct usb_device *dev, + __u16 req, + __u16 value, + __u16 index) +{ + usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, + 500); +} + +static void vc032x_read_sensor_register(struct usb_device *dev, + __u16 address, __u16 *value) +{ + __u8 ldata, mdata, hdata; + __u8 tmpvalue = 0; + int retry = 50; + ldata = 0; + mdata = 0; + hdata = 0; + *value = 0; + + reg_r(dev, 0xa1, 0xb33f, &tmpvalue, 1); + /*PDEBUG(D_PROBE, " I2c Bus Busy Wait 0x%02X ", tmpvalue); */ + if (!(tmpvalue & 0x02)) { + PDEBUG(D_ERR, "I2c Bus Busy Wait %d", tmpvalue & 0x02); + return; + } + reg_w(dev, 0xa0, address, 0xb33a); + reg_w(dev, 0xa0, 0x02, 0xb339); + + tmpvalue = 0; + reg_r(dev, 0xa1, 0xb33b, &tmpvalue, 1); + while (retry-- && tmpvalue) { + reg_r(dev, 0xa1, 0xb33b, &tmpvalue, 1); +/* PDEBUG(D_PROBE, "Read again 0xb33b %d", tmpvalue); */ + msleep(1); + } + reg_r(dev, 0xa1, 0xb33e, &hdata, 1); + reg_r(dev, 0xa1, 0xb33d, &mdata, 1); + reg_r(dev, 0xa1, 0xb33c, &ldata, 1); + PDEBUG(D_PROBE, "Read Sensor h (0x%02X) m (0x%02X) l (0x%02X)", + hdata, mdata, ldata); + tmpvalue = 0; + reg_r(dev, 0xa1, 0xb334, &tmpvalue, 1); + if (tmpvalue == 0x02) + *value = (ldata << 8) + mdata; + else + *value = ldata; +} +static int vc032x_probe_sensor(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + int i; + __u8 data; + __u16 value; + struct sensor_info *ptsensor_info; + + reg_r(dev, 0xa1, 0xbfcf, &data, 1); + PDEBUG(D_PROBE, "check sensor header %d", data); + for (i = 0; i < ARRAY_SIZE(sensor_info_data); i++) { + ptsensor_info = &sensor_info_data[i]; + reg_w(dev, 0xa0, 0x02, 0xb334); + reg_w(dev, 0xa0, ptsensor_info->m1, 0xb300); + reg_w(dev, 0xa0, ptsensor_info->m2, 0xb300); + reg_w(dev, 0xa0, 0x01, 0xb308); + reg_w(dev, 0xa0, 0x0c, 0xb309); + reg_w(dev, 0xa0, ptsensor_info->I2cAdd, 0xb335); +/* PDEBUG(D_PROBE, + "check sensor VC032X -> %d Add -> ox%02X!", + i, ptsensor_info->I2cAdd); */ + reg_w(dev, 0xa0, ptsensor_info->op, 0xb301); + vc032x_read_sensor_register(dev, ptsensor_info->IdAdd, &value); + if (value == ptsensor_info->VpId) { +/* PDEBUG(D_PROBE, "find sensor VC032X -> ox%04X!", + ptsensor_info->VpId); */ + return ptsensor_info->sensorId; + } + } + return -1; +} + +static __u8 i2c_write(struct usb_device *dev, + __u8 reg, __u8 *val, __u8 size) +{ + __u8 retbyte; + + if (size > 3 || size < 1) + return -EINVAL; + reg_r(dev, 0xa1, 0xb33f, &retbyte, 1); + reg_w(dev, 0xa0, size , 0xb334); + reg_w(dev, 0xa0, reg , 0xb33a); + switch (size) { + case 1: + reg_w(dev, 0xa0, val[0] , 0xb336); + break; + case 2: + reg_w(dev, 0xa0, val[0] , 0xb336); + reg_w(dev, 0xa0, val[1] , 0xb337); + break; + case 3: + reg_w(dev, 0xa0, val[0] , 0xb336); + reg_w(dev, 0xa0, val[1] , 0xb337); + reg_w(dev, 0xa0, val[2] , 0xb338); + break; + default: + reg_w(dev, 0xa0, 0x01, 0xb334); + return -EINVAL; + } + reg_w(dev, 0xa0, 0x01, 0xb339); + reg_r(dev, 0xa1, 0xb33b, &retbyte, 1); + return retbyte == 0; +} + +static void put_tab_to_reg(struct gspca_dev *gspca_dev, + __u8 *tab, __u8 tabsize, __u16 addr) +{ + int j; + __u16 ad = addr; + + for (j = 0; j < tabsize; j++) + reg_w(gspca_dev->dev, 0xa0, tab[j], ad++); +} + +static void usb_exchange(struct gspca_dev *gspca_dev, + __u8 data[][4]) +{ + struct usb_device *dev = gspca_dev->dev; + int i = 0; + + for (;;) { + switch (data[i][3]) { + default: + return; + case 0xcc: /* normal write */ + reg_w(dev, 0xa0, data[i][2], + ((data[i][0])<<8) | data[i][1]); + break; + case 0xaa: /* i2c op */ + i2c_write(dev, data[i][1], &data[i][2], 1); + break; + case 0xbb: /* i2c op */ + i2c_write(dev, data[i][0], &data[i][1], 2); + break; + case 0xdd: + msleep(data[i][2] + 10); + break; + } + i++; + } + /*not reached*/ +} + +/* + "GammaT"=hex:04,17,31,4f,6a,83,99,ad,bf,ce,da,e5,ee,f5,fb,ff,ff + "MatrixT"=hex:60,f9,e5,e7,50,05,f3,e6,66 + */ + +static void vc0321_reset(struct gspca_dev *gspca_dev) +{ + reg_w(gspca_dev->dev, 0xa0, 0x00, 0xb04d); + reg_w(gspca_dev->dev, 0xa0, 0x01, 0xb301); + msleep(100); + reg_w(gspca_dev->dev, 0xa0, 0x01, 0xb003); + msleep(100); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + struct cam *cam; + __u8 tmp2[4]; + int sensor; + __u16 vendor; + __u16 product; + + vendor = id->idVendor; + product = id->idProduct; + switch (vendor) { + case 0x046d: /* Logitech Labtec */ +/* switch (product) { */ +/* case 0x0892: */ +/* case 0x0896: */ + sd->bridge = BRIDGE_VC0321; +/* break; */ +/* } */ + break; + case 0x0ac8: /* Vimicro z-star */ + switch (product) { + case 0x0321: + case 0x0328: + case 0xc001: + case 0xc002: + sd->bridge = BRIDGE_VC0321; + break; + case 0x0323: + sd->bridge = BRIDGE_VC0323; + break; + } + break; + case 0x17ef: /* Lenovo */ +/* switch (product) { */ +/* case 0x4802: * Lenovo MI1310_SOC */ + sd->bridge = BRIDGE_VC0323; +/* break; */ +/* } */ + break; + } + + cam = &gspca_dev->cam; + cam->dev_name = (char *) id->driver_info; + cam->epaddr = 0x02; + if (sd->bridge == BRIDGE_VC0321) { + cam->cam_mode = vc0321_mode; + cam->nmodes = sizeof vc0321_mode / sizeof vc0321_mode[0]; + } else { + cam->cam_mode = vc0323_mode; + cam->nmodes = sizeof vc0323_mode / sizeof vc0323_mode[0]; + } + + vc0321_reset(gspca_dev); + sensor = vc032x_probe_sensor(gspca_dev); + switch (sensor) { + case -1: + PDEBUG(D_PROBE, "Unknown sensor..."); + return -EINVAL; + case SENSOR_HV7131R: + PDEBUG(D_PROBE, "Find Sensor HV7131R"); + sd->sensor = SENSOR_HV7131R; + break; + case SENSOR_MI1310_SOC: + PDEBUG(D_PROBE, "Find Sensor MI1310_SOC"); + sd->sensor = SENSOR_MI1310_SOC; + break; + case SENSOR_MI1320: + PDEBUG(D_PROBE, "Find Sensor MI1320"); + sd->sensor = SENSOR_MI1320; + break; + case SENSOR_OV7660: + PDEBUG(D_PROBE, "Find Sensor OV7660"); + sd->sensor = SENSOR_OV7660; + break; + case SENSOR_OV7670: + PDEBUG(D_PROBE, "Find Sensor OV7670"); + sd->sensor = SENSOR_OV7670; + break; + case SENSOR_PO3130NC: + PDEBUG(D_PROBE, "Find Sensor PO3130NC"); + sd->sensor = SENSOR_PO3130NC; + break; + } + + sd->qindex = 7; + sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value; + + if (sd->bridge == BRIDGE_VC0321) { + reg_r(dev, 0x8a, 0, tmp2, 3); + reg_w(dev, 0x87, 0x00, 0x0f0f); + + reg_r(dev, 0x8b, 0, tmp2, 3); + reg_w(dev, 0x88, 0x00, 0x0202); + } + return 0; +} + +/* this function is called at open time */ +static int sd_open(struct gspca_dev *gspca_dev) +{ + return 0; +} + +static void setquality(struct gspca_dev *gspca_dev) +{ +} + +static void setautogain(struct gspca_dev *gspca_dev) +{ +} + +static void setlightfreq(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + static __u8 (*ov7660_freq_tb[3])[4] = + {ov7660_NoFliker, ov7660_50HZ, ov7660_60HZ}; + + if (sd->sensor != SENSOR_OV7660) + return; + usb_exchange(gspca_dev, ov7660_freq_tb[sd->lightfreq]); +} + +static void sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; +/* __u8 tmp2; */ + __u8 *GammaT = NULL; + __u8 *MatrixT = NULL; + int mode; + + /* Assume start use the good resolution from gspca_dev->mode */ + if (sd->bridge == BRIDGE_VC0321) { + reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfec); + reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfed); + reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfee); + reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfef); + } + + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode; + switch (sd->sensor) { + case SENSOR_HV7131R: + GammaT = hv7131r_gamma; + MatrixT = hv7131r_matrix; + if (mode) { + /* 320x240 */ + usb_exchange(gspca_dev, hv7131r_initQVGA_data); + } else { + /* 640x480 */ + usb_exchange(gspca_dev, hv7131r_initVGA_data); + } + break; + case SENSOR_OV7660: + GammaT = ov7660_gamma; + MatrixT = ov7660_matrix; + if (mode) { + /* 320x240 */ + usb_exchange(gspca_dev, ov7660_initQVGA_data); + } else { + /* 640x480 */ + usb_exchange(gspca_dev, ov7660_initVGA_data); + } + break; + case SENSOR_OV7670: + /*GammaT = ov7660_gamma; */ + /*MatrixT = ov7660_matrix; */ + if (mode) { + /* 320x240 */ + usb_exchange(gspca_dev, ov7670_initQVGA_JPG); + } else { + /* 640x480 */ + usb_exchange(gspca_dev, ov7670_initVGA_JPG); + } + break; + case SENSOR_MI1310_SOC: + if (mode) { + /* 320x240 */ + usb_exchange(gspca_dev, mi1310_socinitQVGA_JPG); + } else { + /* 640x480 */ + usb_exchange(gspca_dev, mi1310_socinitVGA_JPG); + } + break; + case SENSOR_MI1320: + GammaT = mi1320_gamma; + MatrixT = mi1320_matrix; + if (mode) { + /* 320x240 */ + usb_exchange(gspca_dev, mi1320_initQVGA_data); + } else { + /* 640x480 */ + usb_exchange(gspca_dev, mi1320_initVGA_data); + } + break; + case SENSOR_PO3130NC: + GammaT = po3130_gamma; + MatrixT = po3130_matrix; + if (mode) { + /* 320x240 */ + usb_exchange(gspca_dev, po3130_initQVGA_data); + } else { + /* 640x480 */ + usb_exchange(gspca_dev, po3130_initVGA_data); + } + usb_exchange(gspca_dev, po3130_rundata); + break; + default: + PDEBUG(D_PROBE, "Damned !! no sensor found Bye"); + return; + } + if (GammaT && MatrixT) { + put_tab_to_reg(gspca_dev, GammaT, 17, 0xb84a); + put_tab_to_reg(gspca_dev, GammaT, 17, 0xb85b); + put_tab_to_reg(gspca_dev, GammaT, 17, 0xb86c); + put_tab_to_reg(gspca_dev, MatrixT, 9, 0xb82c); + + /* Seem SHARPNESS */ + /* + reg_w(gspca_dev->dev, 0xa0, 0x80, 0xb80a); + reg_w(gspca_dev->dev, 0xa0, 0xff, 0xb80b); + reg_w(gspca_dev->dev, 0xa0, 0xff, 0xb80e); + */ + /* all 0x40 ??? do nothing + reg_w(gspca_dev->dev, 0xa0, 0x40, 0xb822); + reg_w(gspca_dev->dev, 0xa0, 0x40, 0xb823); + reg_w(gspca_dev->dev, 0xa0, 0x40, 0xb824); + */ + /* Only works for HV7131R ?? + reg_r (gspca_dev->dev, 0xa1, 0xb881, &tmp2, 1); + reg_w(gspca_dev->dev, 0xa0, 0xfe01, 0xb881); + reg_w(gspca_dev->dev, 0xa0, 0x79, 0xb801); + */ + /* only hv7131r et ov7660 + reg_w(gspca_dev->dev, 0xa0, 0x20, 0xb827); + reg_w(gspca_dev->dev, 0xa0, 0xff, 0xb826); * ISP_GAIN 80 + reg_w(gspca_dev->dev, 0xa0, 0x23, 0xb800); * ISP CTRL_BAS + */ + /* set the led on 0x0892 0x0896 */ + reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff); + msleep(100); + setquality(gspca_dev); + setautogain(gspca_dev); + setlightfreq(gspca_dev); + } +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + + reg_w(dev, 0x89, 0xffff, 0xffff); + reg_w(dev, 0xa0, 0x01, 0xb301); + reg_w(dev, 0xa0, 0x09, 0xb003); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct usb_device *dev = gspca_dev->dev; + + reg_w(dev, 0x89, 0xffff, 0xffff); +} + +/* this function is called at close time */ +static void sd_close(struct gspca_dev *gspca_dev) +{ +/* struct usb_device *dev = gspca_dev->dev; + __u8 buffread; + + reg_w(dev, 0x89, 0xffff, 0xffff); + reg_w(dev, 0xa0, 0x01, 0xb301); + reg_w(dev, 0xa0, 0x09, 0xb303); + reg_w(dev, 0x89, 0xffff, 0xffff); +*/ +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + unsigned char *data, /* isoc packet */ + int len) /* iso pkt length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (data[0] == 0xff && data[1] == 0xd8) { + PDEBUG(D_PACK, + "vc032x header packet found len %d", len); + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, 0); + if (sd->bridge == BRIDGE_VC0321) { +#define VCHDRSZ 46 + data += VCHDRSZ; + len -= VCHDRSZ; +#undef VCHDRSZ + } + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data, len); + return; + } + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + if (gspca_dev->streaming) + setautogain(gspca_dev); + return 0; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->lightfreq = val; + if (gspca_dev->streaming) + setlightfreq(gspca_dev); + return 0; +} + +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->lightfreq; + return 0; +} + +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + switch (menu->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (menu->index) { + case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ + strcpy(menu->name, "NoFliker"); + return 0; + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + strcpy(menu->name, "50 Hz"); + return 0; + case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ + strcpy(menu->name, "60 Hz"); + return 0; + } + break; + } + return -EINVAL; +} + +/* sub-driver description */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .open = sd_open, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .close = sd_close, + .pkt_scan = sd_pkt_scan, + .querymenu = sd_querymenu, +}; + +/* -- module initialisation -- */ +#define DVNM(name) .driver_info = (kernel_ulong_t) name +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x046d, 0x0892), DVNM("Logitech Orbicam")}, + {USB_DEVICE(0x046d, 0x0896), DVNM("Logitech Orbicam")}, + {USB_DEVICE(0x0ac8, 0x0321), DVNM("Vimicro generic vc0321")}, + {USB_DEVICE(0x0ac8, 0x0323), DVNM("Vimicro Vc0323")}, + {USB_DEVICE(0x0ac8, 0x0328), DVNM("A4Tech PK-130MG")}, + {USB_DEVICE(0x0ac8, 0xc001), DVNM("Sony embedded vimicro")}, + {USB_DEVICE(0x0ac8, 0xc002), DVNM("Sony embedded vimicro")}, + {USB_DEVICE(0x17ef, 0x4802), DVNM("Lenovo Vc0323+MI1310_SOC")}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + if (usb_register(&sd_driver) < 0) + return -1; + PDEBUG(D_PROBE, "v%s registered", version); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 03cc7fc58dbc..b767f32511bb 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -3,7 +3,7 @@ * Copyright (C) 2004 2005 2006 Michel Xhaard * mxhaard@magic.fr * - * V4L2 by Jean-François Moine + * V4L2 by Jean-Francois Moine * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,15 +24,14 @@ #include "gspca.h" -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(0, 2, 13) -static const char version[] = "0.2.13"; +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 1, 0) +static const char version[] = "2.1.0"; MODULE_AUTHOR("Michel Xhaard , " "Serge A. Suchkov "); MODULE_DESCRIPTION("GSPCA ZC03xx/VC3xx USB Camera Driver"); MODULE_LICENSE("GPL"); -static int lightfreq = 50; static int force_sensor = -1; #include "jpeg.h" @@ -41,10 +40,12 @@ static int force_sensor = -1; struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - unsigned char brightness; - unsigned char contrast; - unsigned char autogain; - unsigned char gamma; + __u8 brightness; + __u8 contrast; + __u8 gamma; + __u8 autogain; + __u8 lightfreq; + __u8 sharpness; char qindex; char sensor; /* Type of image sensor chip */ @@ -61,14 +62,13 @@ struct sd { #define SENSOR_OV7620 9 /*#define SENSOR_OV7648 9 - same values */ #define SENSOR_OV7630C 10 -/*#define SENSOR_free 11 */ -#define SENSOR_PAS106 12 -#define SENSOR_PB0330 13 -#define SENSOR_PO2030 14 -#define SENSOR_TAS5130CK 15 -#define SENSOR_TAS5130CXX 16 -#define SENSOR_TAS5130C_VF0250 17 -#define SENSOR_MAX 18 +#define SENSOR_PAS106 11 +#define SENSOR_PB0330 12 +#define SENSOR_PO2030 13 +#define SENSOR_TAS5130CK 14 +#define SENSOR_TAS5130CXX 15 +#define SENSOR_TAS5130C_VF0250 16 +#define SENSOR_MAX 17 unsigned short chip_revision; }; @@ -79,7 +79,12 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val); static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { #define SD_BRIGHTNESS 0 @@ -110,7 +115,21 @@ static struct ctrl sd_ctrls[] = { .set = sd_setcontrast, .get = sd_getcontrast, }, -#define SD_AUTOGAIN 2 +#define SD_GAMMA 2 + { + { + .id = V4L2_CID_GAMMA, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gamma", + .minimum = 1, + .maximum = 6, + .step = 1, + .default_value = 4, + }, + .set = sd_setgamma, + .get = sd_getgamma, + }, +#define SD_AUTOGAIN 3 { { .id = V4L2_CID_AUTOGAIN, @@ -124,19 +143,33 @@ static struct ctrl sd_ctrls[] = { .set = sd_setautogain, .get = sd_getautogain, }, -#define SD_GAMMA 3 +#define SD_FREQ 4 { { - .id = V4L2_CID_GAMMA, + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light frequency filter", + .minimum = 0, + .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ + .step = 1, + .default_value = 1, + }, + .set = sd_setfreq, + .get = sd_getfreq, + }, +#define SD_SHARPNESS 5 + { + { + .id = V4L2_CID_SHARPNESS, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gamma", - .minimum = 1, - .maximum = 6, + .name = "Sharpness", + .minimum = 0, + .maximum = 3, .step = 1, - .default_value = 4, + .default_value = 2, }, - .set = sd_setcontrast, - .get = sd_getgamma, + .set = sd_setsharpness, + .get = sd_getsharpness, }, }; @@ -211,11 +244,11 @@ static struct usb_action cs2102_Initial[] = { {0xa1, 0x01, 0x0002}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* 00 */ - {0xa0, 0x08, 0x01c6}, /* clock ? */ + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x24, 0x0120}, /* gamma 5 */ {0xa0, 0x44, 0x0121}, {0xa0, 0x64, 0x0122}, @@ -284,7 +317,7 @@ static struct usb_action cs2102_Initial[] = { {0xa0, 0x40, 0x0116}, {0xa0, 0x40, 0x0117}, {0xa0, 0x40, 0x0118}, - {0, 0, 0} + {} }; static struct usb_action cs2102_InitialScale[] = { @@ -341,11 +374,11 @@ static struct usb_action cs2102_InitialScale[] = { {0xa1, 0x01, 0x0002}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* 00 */ - {0xa0, 0x08, 0x01c6}, /* clock ? */ + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x24, 0x0120}, /* gamma 5 */ {0xa0, 0x44, 0x0121}, {0xa0, 0x64, 0x0122}, @@ -414,7 +447,7 @@ static struct usb_action cs2102_InitialScale[] = { {0xa0, 0x40, 0x0116}, {0xa0, 0x40, 0x0117}, {0xa0, 0x40, 0x0118}, - {0, 0, 0} + {} }; static struct usb_action cs2102_50HZ[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -439,7 +472,7 @@ static struct usb_action cs2102_50HZ[] = { {0xa0, 0x8c, 0x001d}, /* 00,1d,8c,cc */ {0xa0, 0xb0, 0x001e}, /* 00,1e,b0,cc */ {0xa0, 0xd0, 0x001f}, /* 00,1f,d0,cc */ - {0, 0, 0} + {} }; static struct usb_action cs2102_50HZScale[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -464,7 +497,7 @@ static struct usb_action cs2102_50HZScale[] = { {0xa0, 0x93, 0x001d}, /* 00,1d,93,cc */ {0xa0, 0xb0, 0x001e}, /* 00,1e,b0,cc */ {0xa0, 0xd0, 0x001f}, /* 00,1f,d0,cc */ - {0, 0, 0} + {} }; static struct usb_action cs2102_60HZ[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -489,7 +522,7 @@ static struct usb_action cs2102_60HZ[] = { {0xa0, 0x5d, 0x001d}, /* 00,1d,5d,cc */ {0xa0, 0x90, 0x001e}, /* 00,1e,90,cc */ {0xa0, 0xd0, 0x00c8}, /* 00,c8,d0,cc */ - {0, 0, 0} + {} }; static struct usb_action cs2102_60HZScale[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -514,7 +547,7 @@ static struct usb_action cs2102_60HZScale[] = { {0xa0, 0xb7, 0x001d}, /* 00,1d,b7,cc */ {0xa0, 0xd0, 0x001e}, /* 00,1e,d0,cc */ {0xa0, 0xe8, 0x001f}, /* 00,1f,e8,cc */ - {0, 0, 0} + {} }; static struct usb_action cs2102_NoFliker[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -539,7 +572,7 @@ static struct usb_action cs2102_NoFliker[] = { {0xa0, 0x59, 0x001d}, /* 00,1d,59,cc */ {0xa0, 0x90, 0x001e}, /* 00,1e,90,cc */ {0xa0, 0xc8, 0x001f}, /* 00,1f,c8,cc */ - {0, 0, 0} + {} }; static struct usb_action cs2102_NoFlikerScale[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -564,7 +597,7 @@ static struct usb_action cs2102_NoFlikerScale[] = { {0xa0, 0x59, 0x001d}, /* 00,1d,59,cc */ {0xa0, 0x90, 0x001e}, /* 00,1e,90,cc */ {0xa0, 0xc8, 0x001f}, /* 00,1f,c8,cc */ - {0, 0, 0} + {} }; /* CS2102_KOCOM */ @@ -676,8 +709,8 @@ static struct usb_action cs2102K_Initial[] = { {0xa0, 0x40, 0x0117}, {0xa0, 0x4c, 0x0118}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x13, 0x0120}, /* gamma 4 */ {0xa0, 0x38, 0x0121}, {0xa0, 0x59, 0x0122}, @@ -824,7 +857,7 @@ static struct usb_action cs2102K_Initial[] = { {0xa0, 0x00, 0x01a7}, {0xa0, 0x04, 0x01a7}, {0xa0, 0x00, 0x01a7}, - {0, 0, 0} + {} }; static struct usb_action cs2102K_InitialScale[] = { @@ -936,8 +969,8 @@ static struct usb_action cs2102K_InitialScale[] = { {0xa0, 0x40, 0x0117}, {0xa0, 0x4c, 0x0118}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x13, 0x0120}, /* gamma 4 */ {0xa0, 0x38, 0x0121}, {0xa0, 0x59, 0x0122}, @@ -1137,8 +1170,8 @@ static struct usb_action cs2102K_InitialScale[] = { {0xa0, 0x40, 0x0117}, {0xa0, 0x4c, 0x0118}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x13, 0x0120}, /* gamma 4 */ {0xa0, 0x38, 0x0121}, {0xa0, 0x59, 0x0122}, @@ -1401,7 +1434,7 @@ static struct usb_action cs2102K_InitialScale[] = { {0xa0, 0x04, 0x01a7}, {0xa0, 0x00, 0x01a7}, {0xa0, 0x04, 0x01a7}, - {0, 0, 0} + {} }; static struct usb_action gc0305_Initial[] = { /* 640x480 */ @@ -1466,7 +1499,7 @@ static struct usb_action gc0305_Initial[] = { /* 640x480 */ {0xa0, 0x40, 0x0117}, /* 01,17,40,cc */ {0xa0, 0x52, 0x0118}, /* 01,18,52,cc */ {0xa0, 0x03, 0x0113}, /* 01,13,03,cc */ - {0,0,0} + {} }; static struct usb_action gc0305_InitialScale[] = { /* 320x240 */ {0xa0, 0x01, 0x0000}, /* 00,00,01,cc */ @@ -1529,7 +1562,7 @@ static struct usb_action gc0305_InitialScale[] = { /* 320x240 */ {0xa0, 0x40, 0x0117}, /* 01,17,40,cc */ {0xa0, 0x52, 0x0118}, /* 01,18,52,cc */ {0xa0, 0x03, 0x0113}, /* 01,13,03,cc */ - {0,0,0} + {} }; static struct usb_action gc0305_50HZ[] = { {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ @@ -1552,7 +1585,7 @@ static struct usb_action gc0305_50HZ[] = { {0xa0, 0x60, 0x011d}, /* 01,1d,60,cc */ {0xa0, 0x42, 0x0180}, /* 01,80,42,cc */ /* {0xa0, 0x85, 0x018d}, * 01,8d,85,cc * * if 640x480 */ - {0,0,0} + {} }; static struct usb_action gc0305_60HZ[] = { {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ @@ -1575,7 +1608,7 @@ static struct usb_action gc0305_60HZ[] = { {0xa0, 0x60, 0x011d}, /* 01,1d,60,cc */ {0xa0, 0x42, 0x0180}, /* 01,80,42,cc */ {0xa0, 0x80, 0x018d}, /* 01,8d,80,cc */ - {0,0,0} + {} }; static struct usb_action gc0305_NoFliker[] = { @@ -1598,7 +1631,7 @@ static struct usb_action gc0305_NoFliker[] = { {0xa0, 0x60, 0x011d}, /* 01,1d,60,cc */ {0xa0, 0x03, 0x0180}, /* 01,80,03,cc */ {0xa0, 0x80, 0x018d}, /* 01,8d,80,cc */ - {0,0,0} + {} }; /* play poker with registers at your own risk !! */ @@ -1647,11 +1680,11 @@ static struct usb_action hdcs2020xx_Initial[] = { {0xa1, 0x01, 0x0002}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x04, 0x01c6}, + {0xa0, 0x04, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x07, 0x01cb}, + {0xa0, 0x07, 0x01cb}, /* sharpness- */ {0xa0, 0x11, 0x0120}, /* gamma ~4 */ {0xa0, 0x37, 0x0121}, {0xa0, 0x58, 0x0122}, @@ -1744,7 +1777,7 @@ static struct usb_action hdcs2020xx_Initial[] = { {0xa1, 0x01, 0x0118}, /* {0xa0, 0x02, 0x0008}, */ {0xa0, 0x00, 0x0007}, - {0, 0, 0} + {} }; static struct usb_action hdcs2020xx_InitialScale[] = { @@ -1792,11 +1825,11 @@ static struct usb_action hdcs2020xx_InitialScale[] = { {0xa1, 0x01, 0x0002}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x04, 0x01c6}, + {0xa0, 0x04, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x07, 0x01cb}, + {0xa0, 0x07, 0x01cb}, /* sharpness- */ {0xa0, 0x11, 0x0120}, /* gamma ~4*/ {0xa0, 0x37, 0x0121}, {0xa0, 0x58, 0x0122}, @@ -1887,7 +1920,7 @@ static struct usb_action hdcs2020xx_InitialScale[] = { /* {0xa0, 0x02, 0x0008}, */ {0xa0, 0x00, 0x0007}, /* {0xa0, 0x18, 0x00fe}, */ - {0, 0, 0} + {} }; static struct usb_action hdcs2020xb_Initial[] = { {0xa0, 0x01, 0x0000}, @@ -1942,11 +1975,11 @@ static struct usb_action hdcs2020xb_Initial[] = { {0xa0, 0x40, 0x0118}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x13, 0x0120}, /* gamma 4 */ {0xa0, 0x38, 0x0121}, {0xa0, 0x59, 0x0122}, @@ -2019,7 +2052,7 @@ static struct usb_action hdcs2020xb_Initial[] = { {0xa0, 0x40, 0x0116}, {0xa0, 0x40, 0x0117}, {0xa0, 0x40, 0x0118}, - {0, 0, 0} + {} }; static struct usb_action hdcs2020xb_InitialScale[] = { {0xa0, 0x01, 0x0000}, @@ -2072,11 +2105,11 @@ static struct usb_action hdcs2020xb_InitialScale[] = { {0xa0, 0x40, 0x0118}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x13, 0x0120}, /* gamma 4 */ {0xa0, 0x38, 0x0121}, {0xa0, 0x59, 0x0122}, @@ -2147,7 +2180,7 @@ static struct usb_action hdcs2020xb_InitialScale[] = { {0xa0, 0x40, 0x0116}, {0xa0, 0x40, 0x0117}, {0xa0, 0x40, 0x0118}, - {0, 0, 0} + {} }; static struct usb_action hdcs2020b_50HZ[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -2168,7 +2201,7 @@ static struct usb_action hdcs2020b_50HZ[] = { {0xa0, 0x05, 0x001d}, /* 00,1d,05,cc */ {0xa0, 0x1a, 0x001e}, /* 00,1e,1a,cc */ {0xa0, 0x2f, 0x001f}, /* 00,1f,2f,cc */ - {0, 0, 0} + {} }; static struct usb_action hdcs2020b_60HZ[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -2189,7 +2222,7 @@ static struct usb_action hdcs2020b_60HZ[] = { {0xa0, 0x04, 0x001d}, /* 00,1d,04,cc */ {0xa0, 0x18, 0x001e}, /* 00,1e,18,cc */ {0xa0, 0x2c, 0x001f}, /* 00,1f,2c,cc */ - {0, 0, 0} + {} }; static struct usb_action hdcs2020b_NoFliker[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -2210,7 +2243,7 @@ static struct usb_action hdcs2020b_NoFliker[] = { {0xa0, 0x04, 0x001d}, /* 00,1d,04,cc */ {0xa0, 0x17, 0x001e}, /* 00,1e,17,cc */ {0xa0, 0x2a, 0x001f}, /* 00,1f,2a,cc */ - {0, 0, 0} + {} }; static struct usb_action hv7131bxx_Initial[] = { @@ -2266,11 +2299,11 @@ static struct usb_action hv7131bxx_Initial[] = { {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x50, 0x010a}, /* matrix */ {0xa0, 0xf8, 0x010b}, @@ -2318,7 +2351,7 @@ static struct usb_action hv7131bxx_Initial[] = { {0xa0, 0x40, 0x0117}, {0xa0, 0x40, 0x0118}, /* {0xa0, 0x02, 0x0008}, */ - {0, 0, 0} + {} }; static struct usb_action hv7131bxx_InitialScale[] = { @@ -2373,11 +2406,11 @@ static struct usb_action hv7131bxx_InitialScale[] = { {0xa1, 0x01, 0x0096}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x50, 0x010a}, /* matrix */ {0xa0, 0xf8, 0x010b}, @@ -2424,7 +2457,7 @@ static struct usb_action hv7131bxx_InitialScale[] = { {0xa0, 0x40, 0x0117}, {0xa0, 0x40, 0x0118}, /* {0xa0, 0x02, 0x0008}, */ - {0, 0, 0} + {} }; static struct usb_action hv7131cxx_Initial[] = { @@ -2478,11 +2511,11 @@ static struct usb_action hv7131cxx_Initial[] = { {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x60, 0x010a}, /* matrix */ {0xa0, 0xf0, 0x010b}, @@ -2518,7 +2551,7 @@ static struct usb_action hv7131cxx_Initial[] = { {0xa0, 0x40, 0x0180}, {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, - {0, 0, 0} + {} }; static struct usb_action hv7131cxx_InitialScale[] = { @@ -2577,11 +2610,11 @@ static struct usb_action hv7131cxx_InitialScale[] = { {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x60, 0x010a}, /* matrix */ {0xa0, 0xf0, 0x010b}, @@ -2619,7 +2652,7 @@ static struct usb_action hv7131cxx_InitialScale[] = { {0xa0, 0x40, 0x0180}, {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, - {0, 0, 0} + {} }; static struct usb_action icm105axx_Initial[] = { @@ -2743,11 +2776,11 @@ static struct usb_action icm105axx_Initial[] = { {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x52, 0x010a}, /* matrix */ {0xa0, 0xf7, 0x010b}, {0xa0, 0xf7, 0x010c}, @@ -2796,7 +2829,7 @@ static struct usb_action icm105axx_Initial[] = { {0xa0, 0x40, 0x0116}, {0xa0, 0x40, 0x0117}, {0xa0, 0x40, 0x0118}, - {0, 0, 0} + {} }; static struct usb_action icm105axx_InitialScale[] = { @@ -2924,11 +2957,11 @@ static struct usb_action icm105axx_InitialScale[] = { {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x52, 0x010a}, /* matrix */ {0xa0, 0xf7, 0x010b}, @@ -2976,7 +3009,7 @@ static struct usb_action icm105axx_InitialScale[] = { {0xa0, 0x40, 0x0116}, {0xa0, 0x40, 0x0117}, {0xa0, 0x40, 0x0118}, - {0, 0, 0} + {} }; static struct usb_action icm105a_50HZ[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -3007,7 +3040,7 @@ static struct usb_action icm105a_50HZ[] = { {0xa0, 0xd8, 0x001e}, /* 00,1e,d8,cc */ {0xa0, 0xea, 0x001f}, /* 00,1f,ea,cc */ {0xa0, 0xff, 0x0020}, /* 00,20,ff,cc */ - {0, 0, 0} + {} }; static struct usb_action icm105a_50HZScale[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -3040,7 +3073,7 @@ static struct usb_action icm105a_50HZScale[] = { {0xa0, 0xff, 0x0020}, /* 00,20,ff,cc */ {0xa0, 0x00, 0x01a7}, /* 01,a7,00,cc */ {0xa0, 0xc0, 0x01a8}, /* 01,a8,c0,cc */ - {0, 0, 0} + {} }; static struct usb_action icm105a_60HZ[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -3071,7 +3104,7 @@ static struct usb_action icm105a_60HZ[] = { {0xa0, 0xd4, 0x001e}, /* 00,1e,d4,cc */ {0xa0, 0xe8, 0x001f}, /* 00,1f,e8,cc */ {0xa0, 0xff, 0x0020}, /* 00,20,ff,cc */ - {0, 0, 0} + {} }; static struct usb_action icm105a_60HZScale[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -3104,7 +3137,7 @@ static struct usb_action icm105a_60HZScale[] = { {0xa0, 0xff, 0x0020}, /* 00,20,ff,cc */ {0xa0, 0x00, 0x01a7}, /* 01,a7,00,cc */ {0xa0, 0xc0, 0x01a8}, /* 01,a8,c0,cc */ - {0, 0, 0} + {} }; static struct usb_action icm105a_NoFliker[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -3135,7 +3168,7 @@ static struct usb_action icm105a_NoFliker[] = { {0xa0, 0xd4, 0x001e}, /* 00,1e,d4,cc */ {0xa0, 0xe8, 0x001f}, /* 00,1f,e8,cc */ {0xa0, 0xff, 0x0020}, /* 00,20,ff,cc */ - {0, 0, 0} + {} }; static struct usb_action icm105a_NoFlikerScale[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -3168,7 +3201,7 @@ static struct usb_action icm105a_NoFlikerScale[] = { {0xa0, 0xff, 0x0020}, /* 00,20,ff,cc */ {0xa0, 0x00, 0x01a7}, /* 01,a7,00,cc */ {0xa0, 0xc0, 0x01a8}, /* 01,a8,c0,cc */ - {0, 0, 0} + {} }; static struct usb_action MC501CB_InitialScale[] = { @@ -3288,7 +3321,7 @@ static struct usb_action MC501CB_InitialScale[] = { {0xaa, 0x36, 0x001d}, /* 00,36,1D,aa */ {0xaa, 0x37, 0x004c}, /* 00,37,4C,aa */ {0xaa, 0x3b, 0x001d}, /* 00,3B,1D,aa */ - {0, 0, 0} + {} }; static struct usb_action MC501CB_Initial[] = { /* 320x240 */ @@ -3407,7 +3440,7 @@ static struct usb_action MC501CB_Initial[] = { /* 320x240 */ {0xaa, 0x36, 0x001d}, /* 00,36,1D,aa */ {0xaa, 0x37, 0x004c}, /* 00,37,4C,aa */ {0xaa, 0x3b, 0x001d}, /* 00,3B,1D,aa */ - {0, 0, 0} + {} }; static struct usb_action MC501CB_50HZ[] = { @@ -3424,7 +3457,7 @@ static struct usb_action MC501CB_50HZ[] = { {0xaa, 0x36, 0x003a}, /* 00,36,3A,aa */ {0xaa, 0x37, 0x0098}, /* 00,37,98,aa */ {0xaa, 0x3b, 0x003a}, /* 00,3B,3A,aa */ - {0, 0, 0} + {} }; static struct usb_action MC501CB_50HZScale[] = { @@ -3441,7 +3474,7 @@ static struct usb_action MC501CB_50HZScale[] = { {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */ {0xaa, 0x37, 0x006a}, /* 00,37,6A,aa */ {0xaa, 0x3d, 0x0018}, /* 00,3D,18,aa */ - {0, 0, 0} + {} }; static struct usb_action MC501CB_60HZ[] = { @@ -3458,7 +3491,7 @@ static struct usb_action MC501CB_60HZ[] = { {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */ {0xaa, 0x37, 0x00d4}, /* 00,37,D4,aa */ {0xaa, 0x3d, 0x0030}, /* 00,3D,30,aa */ - {0, 0, 0} + {} }; static struct usb_action MC501CB_60HZScale[] = { @@ -3475,7 +3508,7 @@ static struct usb_action MC501CB_60HZScale[] = { {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */ {0xaa, 0x37, 0x006a}, /* 00,37,6A,aa */ {0xaa, 0x3d, 0x0018}, /* 00,3D,18,aa */ - {0, 0, 0} + {} }; static struct usb_action MC501CB_NoFliker[] = { @@ -3492,7 +3525,7 @@ static struct usb_action MC501CB_NoFliker[] = { {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */ {0xaa, 0x37, 0x00d4}, /* 00,37,D4,aa */ {0xaa, 0x3d, 0x0030}, /* 00,3D,30,aa */ - {0, 0, 0} + {} }; static struct usb_action MC501CB_NoFlikerScale[] = { @@ -3504,7 +3537,7 @@ static struct usb_action MC501CB_NoFlikerScale[] = { {0xaa, 0x3e, 0x00d4}, /* 00,3E,D4,aa */ {0xaa, 0x3b, 0x0030}, /* 00,3B,30,aa */ {0xaa, 0x3c, 0x00d4}, /* 00,3C,D4,aa */ - {0, 0, 0} + {} }; /* from zs211.inf - HKR,%OV7620%,Initial - 640x480 */ @@ -3575,7 +3608,7 @@ static struct usb_action OV7620_mode0[] = { {0xa0, 0x40, 0x011d}, /* 01,1d,40,cc */ {0xa0, 0x02, 0x0180}, /* 01,80,02,cc */ {0xa0, 0x50, 0x01a8}, /* 01,a8,50,cc */ - {0, 0, 0} + {} }; /* from zs211.inf - HKR,%OV7620%,InitialScale - 320x240 */ @@ -3646,7 +3679,7 @@ static struct usb_action OV7620_mode1[] = { {0xa0, 0x50, 0x011d}, /* 01,1d,50,cc */ {0xa0, 0x02, 0x0180}, /* 01,80,02,cc */ {0xa0, 0x50, 0x01a8}, /* 01,a8,50,cc */ - {0, 0, 0} + {} }; /* from zs211.inf - HKR,%OV7620%\AE,50HZ */ @@ -3665,7 +3698,7 @@ static struct usb_action OV7620_50HZ[] = { {0xaa, 0x10, 0x0082}, /* 00,10,82,aa */ {0xaa, 0x76, 0x0003}, /* 00,76,03,aa */ /* {0xa0, 0x40, 0x0002}, * 00,02,40,cc - if mode0 (640x480) */ - {0, 0, 0} + {} }; /* from zs211.inf - HKR,%OV7620%\AE,60HZ */ @@ -3687,7 +3720,7 @@ static struct usb_action OV7620_60HZ[] = { /* ?? in gspca v1, it was {0xa0, 0x00, 0x0039}, * 00,00,00,dd * {0xa1, 0x01, 0x0037}, */ - {0, 0, 0} + {} }; /* from zs211.inf - HKR,%OV7620%\AE,NoFliker */ @@ -3707,7 +3740,7 @@ static struct usb_action OV7620_NoFliker[] = { /* ?? was {0xa0, 0x00, 0x0039}, * 00,00,00,dd * {0xa1, 0x01, 0x0037}, */ - {0, 0, 0} + {} }; static struct usb_action ov7630c_Initial[] = { @@ -3795,14 +3828,11 @@ static struct usb_action ov7630c_Initial[] = { /* 0x03, */ {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, -/* 0x05, */ + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, -/* 0x07, */ {0xa1, 0x01, 0x01c9}, -/* 0x0f, */ {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x01, 0x0120}, /* gamma 2 ?*/ {0xa0, 0x0c, 0x0121}, {0xa0, 0x1f, 0x0122}, @@ -3867,7 +3897,7 @@ static struct usb_action ov7630c_Initial[] = { {0xaa, 0x13, 0x0083}, /* 40 */ {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, - {0, 0, 0} + {} }; static struct usb_action ov7630c_InitialScale[] = { @@ -3954,14 +3984,11 @@ static struct usb_action ov7630c_InitialScale[] = { {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, - + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x16, 0x0120}, /* gamma ~4 */ {0xa0, 0x3a, 0x0121}, {0xa0, 0x5b, 0x0122}, @@ -4027,7 +4054,7 @@ static struct usb_action ov7630c_InitialScale[] = { {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, - {0, 0, 0} + {} }; static struct usb_action pas106b_Initial_com[] = { @@ -4041,7 +4068,7 @@ static struct usb_action pas106b_Initial_com[] = { {0xa0, 0x03, 0x003a}, {0xa0, 0x0c, 0x003b}, {0xa0, 0x04, 0x0038}, - {0, 0, 0} + {} }; static struct usb_action pas106b_Initial[] = { /* 176x144 */ @@ -4099,10 +4126,8 @@ static struct usb_action pas106b_Initial[] = { /* 176x144 */ {0xa0, 0x08, 0x0301}, /* EEPROMAccess */ /* JPEG control */ {0xa0, 0x03, 0x0008}, /* ClockSetting */ -/* Unknown */ - {0xa0, 0x08, 0x01c6}, -/* Sharpness */ - {0xa0, 0x0f, 0x01cb}, /* Sharpness05 */ + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ /* Other registers */ {0xa0, 0x0d, 0x0100}, /* OperationMode */ /* Auto exposure and white balance */ @@ -4113,9 +4138,8 @@ static struct usb_action pas106b_Initial[] = { /* 176x144 */ {0xa0, 0x08, 0x0301}, /* EEPROMAccess */ /* JPEG control */ {0xa0, 0x03, 0x0008}, /* ClockSetting */ -/* Sharpness */ - {0xa0, 0x08, 0x01c6}, /* Sharpness00 */ - {0xa0, 0x0f, 0x01cb}, /* Sharpness05 */ + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x58, 0x010a}, /* matrix */ {0xa0, 0xf4, 0x010b}, @@ -4162,7 +4186,7 @@ static struct usb_action pas106b_Initial[] = { /* 176x144 */ {0xa0, 0x40, 0x0116}, /* RGain */ {0xa0, 0x40, 0x0117}, /* GGain */ {0xa0, 0x40, 0x0118}, /* BGain */ - {0, 0, 0} + {} }; static struct usb_action pas106b_InitialScale[] = { /* 352x288 */ @@ -4221,10 +4245,8 @@ static struct usb_action pas106b_InitialScale[] = { /* 352x288 */ {0xa0, 0x08, 0x0301}, /* EEPROMAccess */ /* JPEG control */ {0xa0, 0x03, 0x0008}, /* ClockSetting */ -/* Unknown */ - {0xa0, 0x08, 0x01c6}, -/* Sharpness */ - {0xa0, 0x0f, 0x01cb}, /* Sharpness05 */ + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ /* Other registers */ {0xa0, 0x0d, 0x0100}, /* OperationMode */ /* Auto exposure and white balance */ @@ -4235,9 +4257,8 @@ static struct usb_action pas106b_InitialScale[] = { /* 352x288 */ {0xa0, 0x08, 0x0301}, /* EEPROMAccess */ /* JPEG control */ {0xa0, 0x03, 0x0008}, /* ClockSetting */ -/* Sharpness */ - {0xa0, 0x08, 0x01c6}, /* Sharpness00 */ - {0xa0, 0x0f, 0x01cb}, /* Sharpness05 */ + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x58, 0x010a}, /* matrix */ {0xa0, 0xf4, 0x010b}, @@ -4289,7 +4310,7 @@ static struct usb_action pas106b_InitialScale[] = { /* 352x288 */ {0xa0, 0x00, 0x0007}, /* AutoCorrectEnable */ {0xa0, 0xff, 0x0018}, /* Frame adjust */ - {0, 0, 0} + {} }; static struct usb_action pas106b_50HZ[] = { {0xa0, 0x00, 0x0190}, /* 01,90,00,cc */ @@ -4305,7 +4326,7 @@ static struct usb_action pas106b_50HZ[] = { {0xaa, 0x05, 0x0002}, /* 00,05,02,aa */ {0xaa, 0x07, 0x001c}, /* 00,07,1c,aa */ {0xa0, 0x04, 0x01a9}, /* 01,a9,04,cc */ - {0, 0, 0} + {} }; static struct usb_action pas106b_60HZ[] = { {0xa0, 0x00, 0x0190}, /* 01,90,00,cc */ @@ -4321,7 +4342,7 @@ static struct usb_action pas106b_60HZ[] = { {0xaa, 0x05, 0x0001}, /* 00,05,01,aa */ {0xaa, 0x07, 0x00c4}, /* 00,07,c4,aa */ {0xa0, 0x04, 0x01a9}, /* 01,a9,04,cc */ - {0, 0, 0} + {} }; static struct usb_action pas106b_NoFliker[] = { {0xa0, 0x00, 0x0190}, /* 01,90,00,cc */ @@ -4337,10 +4358,9 @@ static struct usb_action pas106b_NoFliker[] = { {0xaa, 0x05, 0x0001}, /* 00,05,01,aa */ {0xaa, 0x07, 0x0030}, /* 00,07,30,aa */ {0xa0, 0x00, 0x01a9}, /* 01,a9,00,cc */ - {0, 0, 0} + {} }; -/* Aurelien setting from snoop */ static struct usb_action pb03303x_Initial[] = { {0xa0, 0x01, 0x0000}, {0xa0, 0x03, 0x0008}, @@ -4411,11 +4431,11 @@ static struct usb_action pb03303x_Initial[] = { {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x13, 0x0120}, /* gamma 4 */ {0xa0, 0x38, 0x0121}, {0xa0, 0x59, 0x0122}, @@ -4484,7 +4504,7 @@ static struct usb_action pb03303x_Initial[] = { {0xa0, 0x40, 0x0180}, {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, - {0, 0, 0} + {} }; static struct usb_action pb03303x_InitialScale[] = { @@ -4559,11 +4579,11 @@ static struct usb_action pb03303x_InitialScale[] = { {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x13, 0x0120}, /* gamma 4 */ {0xa0, 0x38, 0x0121}, @@ -4633,7 +4653,7 @@ static struct usb_action pb03303x_InitialScale[] = { {0xa0, 0x40, 0x0180}, {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, - {0, 0, 0} + {} }; static struct usb_action pb0330xx_Initial[] = { {0xa1, 0x01, 0x0008}, @@ -4701,11 +4721,11 @@ static struct usb_action pb0330xx_Initial[] = { {0xa0, 0x50, 0x0112}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x50, 0x010a}, /* matrix */ {0xa0, 0xf8, 0x010b}, @@ -4747,7 +4767,7 @@ static struct usb_action pb0330xx_Initial[] = { {0xa1, 0x01, 0x0007}, /* {0xa0, 0x30, 0x0007}, */ /* {0xa0, 0x00, 0x0007}, */ - {0, 0, 0} + {} }; static struct usb_action pb0330xx_InitialScale[] = { @@ -4816,11 +4836,11 @@ static struct usb_action pb0330xx_InitialScale[] = { {0xa0, 0x50, 0x0112}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x50, 0x010a}, /* matrix */ {0xa0, 0xf8, 0x010b}, @@ -4861,7 +4881,7 @@ static struct usb_action pb0330xx_InitialScale[] = { {0xa1, 0x01, 0x0007}, /* {0xa0, 0x30, 0x0007}, */ /* {0xa0, 0x00, 0x0007}, */ - {0, 0, 0} + {} }; static struct usb_action pb0330_50HZ[] = { {0xa0, 0x00, 0x0190}, /* 01,90,00,cc */ @@ -4877,7 +4897,7 @@ static struct usb_action pb0330_50HZ[] = { {0xa0, 0x68, 0x001d}, /* 00,1d,68,cc */ {0xa0, 0x90, 0x001e}, /* 00,1e,90,cc */ {0xa0, 0xc8, 0x001f}, /* 00,1f,c8,cc */ - {0, 0, 0} + {} }; static struct usb_action pb0330_50HZScale[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -4894,7 +4914,7 @@ static struct usb_action pb0330_50HZScale[] = { {0xa0, 0xe5, 0x001d}, /* 00,1d,e5,cc */ {0xa0, 0xf0, 0x001e}, /* 00,1e,f0,cc */ {0xa0, 0xf8, 0x001f}, /* 00,1f,f8,cc */ - {0, 0, 0} + {} }; static struct usb_action pb0330_60HZ[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -4911,7 +4931,7 @@ static struct usb_action pb0330_60HZ[] = { {0xa0, 0x43, 0x001d}, /* 00,1d,43,cc */ {0xa0, 0x50, 0x001e}, /* 00,1e,50,cc */ {0xa0, 0x90, 0x001f}, /* 00,1f,90,cc */ - {0, 0, 0} + {} }; static struct usb_action pb0330_60HZScale[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -4928,7 +4948,7 @@ static struct usb_action pb0330_60HZScale[] = { {0xa0, 0x41, 0x001d}, /* 00,1d,41,cc */ {0xa0, 0x50, 0x001e}, /* 00,1e,50,cc */ {0xa0, 0x90, 0x001f}, /* 00,1f,90,cc */ - {0, 0, 0} + {} }; static struct usb_action pb0330_NoFliker[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -4945,7 +4965,7 @@ static struct usb_action pb0330_NoFliker[] = { {0xa0, 0x09, 0x001d}, /* 00,1d,09,cc */ {0xa0, 0x40, 0x001e}, /* 00,1e,40,cc */ {0xa0, 0x90, 0x001f}, /* 00,1f,90,cc */ - {0, 0, 0} + {} }; static struct usb_action pb0330_NoFlikerScale[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -4962,7 +4982,7 @@ static struct usb_action pb0330_NoFlikerScale[] = { {0xa0, 0x09, 0x001d}, /* 00,1d,09,cc */ {0xa0, 0x40, 0x001e}, /* 00,1e,40,cc */ {0xa0, 0x90, 0x001f}, /* 00,1f,90,cc */ - {0, 0, 0} + {} }; /* from oem9.inf - HKR,%PO2030%,Initial - 640x480 - (close to CS2102) */ @@ -5039,7 +5059,7 @@ static struct usb_action PO2030_mode0[] = { {0xa0, 0x08, 0x0301}, /* 03,01,08,cc */ {0xa0, 0x7a, 0x0116}, /* 01,16,7a,cc */ {0xa0, 0x4a, 0x0118}, /* 01,18,4a,cc */ - {0, 0, 0} + {} }; /* from oem9.inf - HKR,%PO2030%,InitialScale - 320x240 */ @@ -5116,7 +5136,7 @@ static struct usb_action PO2030_mode1[] = { {0xa0, 0x08, 0x0301}, /* 03,01,08,cc */ {0xa0, 0x7a, 0x0116}, /* 01,16,7a,cc */ {0xa0, 0x4a, 0x0118}, /* 01,18,4a,cc */ - {0, 0, 0} + {} }; static struct usb_action PO2030_50HZ[] = { @@ -5138,7 +5158,7 @@ static struct usb_action PO2030_50HZ[] = { {0xa0, 0x88, 0x018d}, /* 01,8d,88,cc */ {0xa0, 0x58, 0x011d}, /* 01,1d,58,cc */ {0xa0, 0x42, 0x0180}, /* 01,80,42,cc */ - {0, 0, 0} + {} }; static struct usb_action PO2030_60HZ[] = { @@ -5160,7 +5180,7 @@ static struct usb_action PO2030_60HZ[] = { {0xa0, 0x88, 0x018d}, /* 01,8d,88,cc */ /* win: 01,8d,80 */ {0xa0, 0x58, 0x011d}, /* 01,1d,58,cc */ {0xa0, 0x42, 0x0180}, /* 01,80,42,cc */ - {0, 0, 0} + {} }; static struct usb_action PO2030_NoFliker[] = { @@ -5171,7 +5191,7 @@ static struct usb_action PO2030_NoFliker[] = { {0xaa, 0x1c, 0x0078}, /* 00,1c,78,aa */ {0xaa, 0x46, 0x0000}, /* 00,46,00,aa */ {0xaa, 0x15, 0x0000}, /* 00,15,00,aa */ - {0, 0, 0} + {} }; /* TEST */ @@ -5302,8 +5322,8 @@ static struct usb_action tas5130CK_Initial[] = { {0xa0, 0x03, 0x0111}, {0xa0, 0x51, 0x0112}, {0xa0, 0x03, 0x0008}, - {0xa0, 0x08, 0x01c6}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x38, 0x0120}, /* gamma > 5 */ {0xa0, 0x51, 0x0121}, {0xa0, 0x6e, 0x0122}, @@ -5375,7 +5395,7 @@ static struct usb_action tas5130CK_Initial[] = { {0xa0, 0x15, 0x01ae}, {0xa0, 0x40, 0x0180}, {0xa0, 0x42, 0x0180}, - {0, 0, 0} + {} }; static struct usb_action tas5130CK_InitialScale[] = { @@ -5505,8 +5525,8 @@ static struct usb_action tas5130CK_InitialScale[] = { {0xa0, 0x03, 0x0111}, {0xa0, 0x51, 0x0112}, {0xa0, 0x03, 0x0008}, - {0xa0, 0x08, 0x01c6}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x38, 0x0120}, /* gamma > 5 */ {0xa0, 0x51, 0x0121}, {0xa0, 0x6e, 0x0122}, @@ -5583,7 +5603,7 @@ static struct usb_action tas5130CK_InitialScale[] = { {0xa0, 0x02, 0x0008}, {0xa0, 0x00, 0x0007}, {0xa0, 0x03, 0x0008}, - {0, 0, 0} + {} }; static struct usb_action tas5130cxx_Initial[] = { @@ -5625,11 +5645,11 @@ static struct usb_action tas5130cxx_Initial[] = { {0xa1, 0x01, 0x0002}, {0xa1, 0x01, 0x0008}, {0xa0, 0x03, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x68, 0x010a}, /* matrix */ {0xa0, 0xec, 0x010b}, @@ -5673,7 +5693,7 @@ static struct usb_action tas5130cxx_Initial[] = { {0xa0, 0x40, 0x0180}, {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, - {0, 0, 0} + {} }; static struct usb_action tas5130cxx_InitialScale[] = { {0xa0, 0x01, 0x0000}, @@ -5718,11 +5738,11 @@ static struct usb_action tas5130cxx_InitialScale[] = { {0xa0, 0x03, 0x0008}, {0xa1, 0x01, 0x0008}, /* clock ? */ - {0xa0, 0x08, 0x01c6}, + {0xa0, 0x08, 0x01c6}, /* sharpness+ */ {0xa1, 0x01, 0x01c8}, {0xa1, 0x01, 0x01c9}, {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, 0x01cb}, + {0xa0, 0x0f, 0x01cb}, /* sharpness- */ {0xa0, 0x68, 0x010a}, /* matrix */ {0xa0, 0xec, 0x010b}, @@ -5763,7 +5783,7 @@ static struct usb_action tas5130cxx_InitialScale[] = { {0xa0, 0x40, 0x0180}, {0xa1, 0x01, 0x0180}, {0xa0, 0x42, 0x0180}, - {0, 0, 0} + {} }; static struct usb_action tas5130cxx_50HZ[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -5786,7 +5806,7 @@ static struct usb_action tas5130cxx_50HZ[] = { {0xa0, 0xea, 0x001f}, /* 00,1f,ea,cc */ {0xa0, 0xff, 0x0020}, /* 00,20,ff,cc */ {0xa0, 0x03, 0x009f}, /* 00,9f,03,cc */ - {0, 0, 0} + {} }; static struct usb_action tas5130cxx_50HZScale[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -5809,7 +5829,7 @@ static struct usb_action tas5130cxx_50HZScale[] = { {0xa0, 0xf8, 0x001f}, /* 00,1f,f8,cc */ {0xa0, 0xff, 0x0020}, /* 00,20,ff,cc */ {0xa0, 0x03, 0x009f}, /* 00,9f,03,cc */ - {0, 0, 0} + {} }; static struct usb_action tas5130cxx_60HZ[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -5832,7 +5852,7 @@ static struct usb_action tas5130cxx_60HZ[] = { {0xa0, 0xe0, 0x001f}, /* 00,1f,e0,cc */ {0xa0, 0xff, 0x0020}, /* 00,20,ff,cc */ {0xa0, 0x03, 0x009f}, /* 00,9f,03,cc */ - {0, 0, 0} + {} }; static struct usb_action tas5130cxx_60HZScale[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -5855,7 +5875,7 @@ static struct usb_action tas5130cxx_60HZScale[] = { {0xa0, 0xe0, 0x001f}, /* 00,1f,e0,cc */ {0xa0, 0xff, 0x0020}, /* 00,20,ff,cc */ {0xa0, 0x03, 0x009f}, /* 00,9f,03,cc */ - {0, 0, 0} + {} }; static struct usb_action tas5130cxx_NoFliker[] = { {0xa0, 0x00, 0x0019}, /* 00,19,00,cc */ @@ -5878,7 +5898,7 @@ static struct usb_action tas5130cxx_NoFliker[] = { {0xa0, 0xe0, 0x001f}, /* 00,1f,e0,cc */ {0xa0, 0xff, 0x0020}, /* 00,20,ff,cc */ {0xa0, 0x02, 0x009f}, /* 00,9f,02,cc */ - {0, 0, 0} + {} }; static struct usb_action tas5130cxx_NoFlikerScale[] = { @@ -5902,7 +5922,7 @@ static struct usb_action tas5130cxx_NoFlikerScale[] = { {0xa0, 0xe0, 0x001f}, /* 00,1f,e0,cc */ {0xa0, 0xff, 0x0020}, /* 00,20,ff,cc */ {0xa0, 0x02, 0x009f}, /* 00,9f,02,cc */ - {0, 0, 0} + {} }; static struct usb_action tas5130c_vf0250_Initial[] = { @@ -5966,7 +5986,7 @@ static struct usb_action tas5130c_vf0250_Initial[] = { {0xa0, 0x60, 0x01a8}, /* 01,a8,60,cc, */ {0xa0, 0x61, 0x0116}, /* 01,16,61,cc, */ {0xa0, 0x65, 0x0118}, /* 01,18,65,cc */ - {0, 0, 0} + {} }; static struct usb_action tas5130c_vf0250_InitialScale[] = { @@ -6030,7 +6050,7 @@ static struct usb_action tas5130c_vf0250_InitialScale[] = { {0xa0, 0x60, 0x01a8}, /* 01,a8,60,cc, */ {0xa0, 0x61, 0x0116}, /* 01,16,61,cc, */ {0xa0, 0x65, 0x0118}, /* 01,18,65,cc */ - {0, 0, 0} + {} }; /* "50HZ" light frequency banding filter */ static struct usb_action tas5130c_vf0250_50HZ[] = { @@ -6054,7 +6074,7 @@ static struct usb_action tas5130c_vf0250_50HZ[] = { {0xa0, 0x58, 0x011d}, /* 01,1d,58,cc, */ {0xa0, 0x42, 0x0180}, /* 01,80,42,cc, */ {0xa0, 0x78, 0x018d}, /* 01,8d,78,cc */ - {0, 0, 0} + {} }; /* "50HZScale" light frequency banding filter */ @@ -6079,7 +6099,7 @@ static struct usb_action tas5130c_vf0250_50HZScale[] = { {0xa0, 0x58, 0x011d}, /* 01,1d,58,cc, */ {0xa0, 0x42, 0x0180}, /* 01,80,42,cc, */ {0xa0, 0x78, 0x018d}, /* 01,8d,78,cc */ - {0, 0, 0} + {} }; /* "60HZ" light frequency banding filter */ @@ -6104,7 +6124,7 @@ static struct usb_action tas5130c_vf0250_60HZ[] = { {0xa0, 0x58, 0x011d}, /* 01,1d,58,cc, */ {0xa0, 0x42, 0x0180}, /* 01,80,42,cc, */ {0xa0, 0x78, 0x018d}, /* 01,8d,78,cc */ - {0, 0, 0} + {} }; /* "60HZScale" light frequency banding ilter */ @@ -6129,7 +6149,7 @@ static struct usb_action tas5130c_vf0250_60HZScale[] = { {0xa0, 0x58, 0x011d}, /* 01,d,58,cc, */ {0xa0, 0x42, 0x0180}, /* 01,80,42,cc, */ {0xa0, 0x78, 0x018d}, /* 01,d,78,cc */ - {0, 0, 0} + {} }; /* "NoFliker" light frequency banding flter */ @@ -6152,7 +6172,7 @@ static struct usb_action tas5130c_vf0250_NoFliker[] = { {0xa0, 0xff, 0x0020}, /* 00,20,ff,cc, */ {0xa0, 0x58, 0x011d}, /* 01,1d,58,cc, */ {0xa0, 0x03, 0x0180}, /* 01,80,03,cc */ - {0, 0, 0} + {} }; /* "NoFlikerScale" light frequency banding filter */ @@ -6175,7 +6195,7 @@ static struct usb_action tas5130c_vf0250_NoFlikerScale[] = { {0xa0, 0xff, 0x0020}, /* 00,20,ff,cc, */ {0xa0, 0x58, 0x011d}, /* 01,1d,58,cc, */ {0xa0, 0x03, 0x0180}, /* 01,80,03,cc */ - {0, 0, 0} + {} }; static void reg_r_i(struct usb_device *dev, @@ -6325,7 +6345,7 @@ static void setbrightness(struct gspca_dev *gspca_dev) case SENSOR_PO2030: return; } -/*fixme: is it really 011d 018d for all other sensors? */ +/*fixme: is it really write to 011d and 018d for all other sensors? */ brightness = sd->brightness; reg_w(gspca_dev->dev, brightness, 0x011d); if (brightness < 0x70) @@ -6348,20 +6368,7 @@ static void setsharpness(struct gspca_dev *gspca_dev) {0x10, 0x1e} }; - switch (sd->sensor) { - case SENSOR_GC0305: - sharpness = 3; - break; - case SENSOR_OV7620: - sharpness = 2; - break; - case SENSOR_PO2030: - sharpness = 0; - break; - default: - return; - } -/*fixme: sharpness set by V4L2_CID_SATURATION?*/ + sharpness = sd->sharpness; reg_w(dev, sharpness_tb[sharpness][0], 0x01c6); reg_r(dev, 0x01c8, &retbyte); reg_r(dev, 0x01c9, &retbyte); @@ -6411,7 +6418,7 @@ static void setcontrast(struct gspca_dev *gspca_dev) static __u8 Tgradient_5[16] = {0x37, 0x26, 0x20, 0x1a, 0x14, 0x10, 0x0e, 0x0b, 0x09, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x02}; - static __u8 Tgamma_6[16] = /* ?? was gama 5 */ + static __u8 Tgamma_6[16] = /* ?? was gamma 5 */ {0x24, 0x44, 0x64, 0x84, 0x9d, 0xb2, 0xc4, 0xd3, 0xe0, 0xeb, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff}; static __u8 Tgradient_6[16] = @@ -6425,7 +6432,7 @@ static void setcontrast(struct gspca_dev *gspca_dev) 0, Tgradient_1, Tgradient_2, Tgradient_3, Tgradient_4, Tgradient_5, Tgradient_6 }; -#ifdef GSPCA_DEBUG +#ifdef VIDEO_ADV_DEBUG __u8 v[16]; #endif @@ -6443,7 +6450,7 @@ static void setcontrast(struct gspca_dev *gspca_dev) else if (g <= 0) g = 1; reg_w(dev, g, 0x0120 + i); /* gamma */ -#ifdef GSPCA_DEBUG +#ifdef VIDEO_ADV_DEBUG if (gspca_debug & D_CONF) v[i] = g; #endif @@ -6463,7 +6470,7 @@ static void setcontrast(struct gspca_dev *gspca_dev) g = 1; } reg_w(dev, g, 0x0130 + i); /* gradient */ -#ifdef GSPCA_DEBUG +#ifdef VIDEO_ADV_DEBUG if (gspca_debug & D_CONF) v[i] = g; #endif @@ -6488,7 +6495,7 @@ static void setquality(struct gspca_dev *gspca_dev) return; } /*fixme: is it really 0008 0007 0018 for all other sensors? */ - quality = sd->qindex & 0x0f; + quality = sd->qindex; reg_w(dev, quality, 0x0008); frxt = 0x30; reg_w(dev, frxt, 0x0007); @@ -6525,25 +6532,25 @@ static int setlightfreq(struct gspca_dev *gspca_dev) struct usb_action *zc3_freq; static struct usb_action *freq_tb[SENSOR_MAX][6] = { /* SENSOR_CS2102 0 */ - {cs2102_50HZ, cs2102_50HZScale, - cs2102_60HZ, cs2102_60HZScale, - cs2102_NoFliker, cs2102_NoFlikerScale}, + {cs2102_NoFliker, cs2102_NoFlikerScale, + cs2102_50HZ, cs2102_50HZScale, + cs2102_60HZ, cs2102_60HZScale}, /* SENSOR_CS2102K 1 */ - {cs2102_50HZ, cs2102_50HZScale, - cs2102_60HZ, cs2102_60HZScale, - cs2102_NoFliker, cs2102_NoFlikerScale}, + {cs2102_NoFliker, cs2102_NoFlikerScale, + cs2102_50HZ, cs2102_50HZScale, + cs2102_60HZ, cs2102_60HZScale}, /* SENSOR_GC0305 2 */ - {gc0305_50HZ, gc0305_50HZ, - gc0305_60HZ, gc0305_60HZ, - gc0305_NoFliker, gc0305_NoFliker}, + {gc0305_NoFliker, gc0305_NoFliker, + gc0305_50HZ, gc0305_50HZ, + gc0305_60HZ, gc0305_60HZ}, /* SENSOR_HDCS2020 3 */ {0, 0, 0, 0, 0, 0}, /* SENSOR_HDCS2020b 4 */ - {hdcs2020b_50HZ, hdcs2020b_50HZ, - hdcs2020b_60HZ, hdcs2020b_60HZ, - hdcs2020b_NoFliker, hdcs2020b_NoFliker}, + {hdcs2020b_NoFliker, hdcs2020b_NoFliker, + hdcs2020b_50HZ, hdcs2020b_50HZ, + hdcs2020b_60HZ, hdcs2020b_60HZ}, /* SENSOR_HV7131B 5 */ {0, 0, 0, 0, @@ -6553,66 +6560,48 @@ static int setlightfreq(struct gspca_dev *gspca_dev) 0, 0, 0, 0}, /* SENSOR_ICM105A 7 */ - {icm105a_50HZ, icm105a_50HZScale, - icm105a_60HZ, icm105a_60HZScale, - icm105a_NoFliker, icm105a_NoFlikerScale}, + {icm105a_NoFliker, icm105a_NoFlikerScale, + icm105a_50HZ, icm105a_50HZScale, + icm105a_60HZ, icm105a_60HZScale}, /* SENSOR_MC501CB 8 */ - {MC501CB_50HZ, MC501CB_50HZScale, - MC501CB_60HZ, MC501CB_60HZScale, - MC501CB_NoFliker, MC501CB_NoFlikerScale}, + {MC501CB_NoFliker, MC501CB_NoFlikerScale, + MC501CB_50HZ, MC501CB_50HZScale, + MC501CB_60HZ, MC501CB_60HZScale}, /* SENSOR_OV7620 9 */ - {OV7620_50HZ, OV7620_50HZ, - OV7620_60HZ, OV7620_60HZ, - OV7620_NoFliker, OV7620_NoFliker}, + {OV7620_NoFliker, OV7620_NoFliker, + OV7620_50HZ, OV7620_50HZ, + OV7620_60HZ, OV7620_60HZ}, /* SENSOR_OV7630C 10 */ {0, 0, 0, 0, 0, 0}, -/* SENSOR_free 11 */ - {0, 0, - 0, 0, - 0, 0}, -/* SENSOR_PAS106 12 */ - {pas106b_50HZ, pas106b_50HZ, - pas106b_60HZ, pas106b_60HZ, - pas106b_NoFliker, pas106b_NoFliker}, -/* SENSOR_PB0330 13 */ - {pb0330_50HZ, pb0330_50HZScale, - pb0330_60HZ, pb0330_60HZScale, - pb0330_NoFliker, pb0330_NoFlikerScale}, -/* SENSOR_PO2030 14 */ - {PO2030_50HZ, PO2030_50HZ, - PO2030_60HZ, PO2030_60HZ, - PO2030_NoFliker, PO2030_NoFliker}, -/* SENSOR_TAS5130CK 15 */ - {tas5130cxx_50HZ, tas5130cxx_50HZScale, - tas5130cxx_60HZ, tas5130cxx_60HZScale, - tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale}, -/* SENSOR_TAS5130CXX 16 */ - {tas5130cxx_50HZ, tas5130cxx_50HZScale, - tas5130cxx_60HZ, tas5130cxx_60HZScale, - tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale}, -/* SENSOR_TAS5130C_VF0250 17 */ - {tas5130c_vf0250_50HZ, tas5130c_vf0250_50HZScale, - tas5130c_vf0250_60HZ, tas5130c_vf0250_60HZScale, - tas5130c_vf0250_NoFliker, tas5130c_vf0250_NoFlikerScale}, +/* SENSOR_PAS106 11 */ + {pas106b_NoFliker, pas106b_NoFliker, + pas106b_50HZ, pas106b_50HZ, + pas106b_60HZ, pas106b_60HZ}, +/* SENSOR_PB0330 12 */ + {pb0330_NoFliker, pb0330_NoFlikerScale, + pb0330_50HZ, pb0330_50HZScale, + pb0330_60HZ, pb0330_60HZScale}, +/* SENSOR_PO2030 13 */ + {PO2030_NoFliker, PO2030_NoFliker, + PO2030_50HZ, PO2030_50HZ, + PO2030_60HZ, PO2030_60HZ}, +/* SENSOR_TAS5130CK 14 */ + {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale, + tas5130cxx_50HZ, tas5130cxx_50HZScale, + tas5130cxx_60HZ, tas5130cxx_60HZScale}, +/* SENSOR_TAS5130CXX 15 */ + {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale, + tas5130cxx_50HZ, tas5130cxx_50HZScale, + tas5130cxx_60HZ, tas5130cxx_60HZScale}, +/* SENSOR_TAS5130C_VF0250 16 */ + {tas5130c_vf0250_NoFliker, tas5130c_vf0250_NoFlikerScale, + tas5130c_vf0250_50HZ, tas5130c_vf0250_50HZScale, + tas5130c_vf0250_60HZ, tas5130c_vf0250_60HZScale}, }; - switch (lightfreq) { - case 50: - i = 0; - break; - case 60: - i = 2; - break; - default: - PDEBUG(D_ERR, "Invalid light freq value %d", lightfreq); - lightfreq = 0; /* set to default filter value */ - /* fall thru */ - case 0: - i = 4; - break; - } + i = sd->lightfreq * 2; mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode; if (!mode) i++; /* 640x480 */ @@ -6622,13 +6611,13 @@ static int setlightfreq(struct gspca_dev *gspca_dev) switch (sd->sensor) { case SENSOR_GC0305: if (mode /* if 320x240 */ - && lightfreq == 50) + && sd->lightfreq == 1) /* and 50Hz */ reg_w(gspca_dev->dev, 0x85, 0x018d); /* win: 0x80, 0x018d */ break; case SENSOR_OV7620: if (!mode) { /* if 640x480 */ - if (lightfreq != 0) /* 50 or 60 Hz */ + if (sd->lightfreq != 0) /* and 50 or 60 Hz */ reg_w(gspca_dev->dev, 0x40, 0x0002); else reg_w(gspca_dev->dev, 0x44, 0x0002); @@ -6653,9 +6642,9 @@ static void setautogain(struct gspca_dev *gspca_dev) static void send_unknown(struct usb_device *dev, int sensor) { + reg_w(dev, 0x01, 0x0000); /* led off */ switch (sensor) { case SENSOR_PAS106: - reg_w(dev, 0x01, 0x0000); reg_w(dev, 0x03, 0x003a); reg_w(dev, 0x0c, 0x003b); reg_w(dev, 0x08, 0x0038); @@ -6664,7 +6653,6 @@ static void send_unknown(struct usb_device *dev, int sensor) case SENSOR_OV7620: case SENSOR_PB0330: case SENSOR_PO2030: - reg_w(dev, 0x01, 0x0000); reg_w(dev, 0x0d, 0x003a); reg_w(dev, 0x02, 0x003b); reg_w(dev, 0x00, 0x0038); @@ -6817,7 +6805,7 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev) /*fixme: lack of 8b=b3 (11,12)-> 10, 8b=e0 (14,15,16)-> 12 found in gspcav1*/ reg_w(dev, 0x02, 0x0010); - reg_r(dev, 0x0010, &retbyte); + reg_r(dev, 0x10, &retbyte); reg_w(dev, 0x01, 0x0000); reg_w(dev, 0x00, 0x0010); reg_w(dev, 0x01, 0x0001); @@ -6964,7 +6952,7 @@ static int sd_config(struct gspca_dev *gspca_dev, int sensor; __u8 bsensor; int vga = 1; /* 1: vga, 0: sif */ - static unsigned char gamma[SENSOR_MAX] = { + static __u8 gamma[SENSOR_MAX] = { 5, /* SENSOR_CS2102 0 */ 5, /* SENSOR_CS2102K 1 */ 4, /* SENSOR_GC0305 2 */ @@ -6976,16 +6964,16 @@ static int sd_config(struct gspca_dev *gspca_dev, 4, /* SENSOR_MC501CB 8 */ 3, /* SENSOR_OV7620 9 */ 4, /* SENSOR_OV7630C 10 */ - 4, /* SENSOR_free 11 */ - 4, /* SENSOR_PAS106 12 */ - 4, /* SENSOR_PB0330 13 */ - 4, /* SENSOR_PO2030 14 */ - 4, /* SENSOR_TAS5130CK 15 */ - 4, /* SENSOR_TAS5130CXX 16 */ - 3, /* SENSOR_TAS5130C_VF0250 17 */ + 4, /* SENSOR_PAS106 11 */ + 4, /* SENSOR_PB0330 12 */ + 4, /* SENSOR_PO2030 13 */ + 4, /* SENSOR_TAS5130CK 14 */ + 4, /* SENSOR_TAS5130CXX 15 */ + 3, /* SENSOR_TAS5130C_VF0250 16 */ }; /* define some sensors from the vendor/product */ + sd->sharpness = 2; switch (id->idVendor) { case 0x041e: /* Creative */ switch (id->idProduct) { @@ -7055,8 +7043,9 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->sensor = SENSOR_ICM105A; break; case 0x0e: - PDEBUG(D_PROBE, "Find Sensor PAS202BCB"); + PDEBUG(D_PROBE, "Find Sensor HDCS2020"); sd->sensor = SENSOR_HDCS2020; + sd->sharpness = 1; break; case 0x0f: PDEBUG(D_PROBE, "Find Sensor PAS106"); @@ -7097,6 +7086,7 @@ static int sd_config(struct gspca_dev *gspca_dev, case 0x2030: PDEBUG(D_PROBE, "Find Sensor PO2030"); sd->sensor = SENSOR_PO2030; + sd->sharpness = 0; /* from win traces */ break; case 0x7620: PDEBUG(D_PROBE, "Find Sensor OV7620"); @@ -7134,13 +7124,13 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->qindex = 1; sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; - sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value; sd->gamma = gamma[(int) sd->sensor]; + sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value; + sd->lightfreq = sd_ctrls[SD_FREQ].qctrl.default_value; + sd->sharpness = sd_ctrls[SD_SHARPNESS].qctrl.default_value; /* switch the led off */ -/*fixme: other sensors? */ - if (sensor == 0x06 || sensor == 0x11) - reg_w(gspca_dev->dev, 0x01, 0x0000); + reg_w(gspca_dev->dev, 0x01, 0x0000); return 0; } @@ -7170,15 +7160,14 @@ static void sd_start(struct gspca_dev *gspca_dev) {MC501CB_InitialScale, MC501CB_Initial}, /* 9 */ {OV7620_mode0, OV7620_mode1}, /* 9 */ {ov7630c_InitialScale, ov7630c_Initial}, /* 10 */ - {0, 0}, /* 11 */ - {pas106b_InitialScale, pas106b_Initial}, /* 12 */ - {pb0330xx_InitialScale, pb0330xx_Initial}, /* 13 */ + {pas106b_InitialScale, pas106b_Initial}, /* 11 */ + {pb0330xx_InitialScale, pb0330xx_Initial}, /* 12 */ /* or {pb03303x_InitialScale, pb03303x_Initial}, */ - {PO2030_mode0, PO2030_mode1}, /* 14 */ - {tas5130CK_InitialScale, tas5130CK_Initial}, /* 15 */ - {tas5130cxx_InitialScale, tas5130cxx_Initial}, /* 16 */ + {PO2030_mode0, PO2030_mode1}, /* 13 */ + {tas5130CK_InitialScale, tas5130CK_Initial}, /* 14 */ + {tas5130cxx_InitialScale, tas5130cxx_Initial}, /* 15 */ {tas5130c_vf0250_InitialScale, tas5130c_vf0250_Initial}, - /* 17 */ + /* 16 */ }; mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].mode; @@ -7324,7 +7313,7 @@ static void sd_close(struct gspca_dev *gspca_dev) static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, - unsigned char *data, + __u8 *data, int len) { @@ -7401,6 +7390,16 @@ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gamma = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return 0; +} + static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; @@ -7409,6 +7408,63 @@ static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->lightfreq = val; + if (gspca_dev->streaming) + setlightfreq(gspca_dev); + return 0; +} + +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->lightfreq; + return 0; +} + +static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->sharpness = val; + if (gspca_dev->streaming) + setsharpness(gspca_dev); + return 0; +} + +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->sharpness; + return 0; +} + +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + switch (menu->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (menu->index) { + case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ + strcpy(menu->name, "NoFliker"); + return 0; + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + strcpy(menu->name, "50 Hz"); + return 0; + case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ + strcpy(menu->name, "60 Hz"); + return 0; + } + break; + } + return -EINVAL; +} + static struct sd_desc sd_desc = { .name = MODULE_NAME, .ctrls = sd_ctrls, @@ -7420,6 +7476,7 @@ static struct sd_desc sd_desc = { .stop0 = sd_stop0, .close = sd_close, .pkt_scan = sd_pkt_scan, + .querymenu = sd_querymenu, }; #define DVNM(name) .driver_info = (kernel_ulong_t) name @@ -7514,10 +7571,6 @@ static void __exit sd_mod_exit(void) module_init(sd_mod_init); module_exit(sd_mod_exit); -module_param(lightfreq, int, 0644); -MODULE_PARM_DESC(lightfreq, - "Light frequency banding filter: 50, 60 Hz or" - " 0 to NoFliker (default=50)"); module_param(force_sensor, int, 0644); MODULE_PARM_DESC(force_sensor, "Force sensor. Only for experts!!!"); diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 9385c823a978..43a6c81a53ef 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -324,6 +324,8 @@ struct v4l2_pix_format #define V4L2_PIX_FMT_PWC1 v4l2_fourcc('P','W','C','1') /* pwc older webcam */ #define V4L2_PIX_FMT_PWC2 v4l2_fourcc('P','W','C','2') /* pwc newer webcam */ #define V4L2_PIX_FMT_ET61X251 v4l2_fourcc('E','6','2','5') /* ET61X251 compression */ +#define V4L2_PIX_FMT_SPCA501 v4l2_fourcc('S','5','0','1') /* YUYV per line */ +#define V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S','5','6','1') /* compressed BGGR bayer */ /* * F O R M A T E N U M E R A T I O N -- cgit v1.2.3 From 54ab92ca05550550bcec2462de2605f35d079b66 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 3 Jul 2008 11:20:58 -0300 Subject: V4L/DVB (8194): gspca: Fix the format of the low resolution mode of spca561. The low (half) res modes of the spca561 are not spca561 compressed, but are raw bayer, this patches fixes this and adds a PIX_FMT define for the GBRG bayer format used by the spca561 in low res mode. Signed-off-by: Hans de Goede Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/spca561.c | 6 +++--- include/linux/videodev2.h | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c index a94e6270115e..dbd1648a655f 100644 --- a/drivers/media/video/gspca/spca561.c +++ b/drivers/media/video/gspca/spca561.c @@ -98,8 +98,8 @@ static struct ctrl sd_ctrls[] = { }; static struct cam_mode sif_mode[] = { - {V4L2_PIX_FMT_SPCA561, 160, 120, 3}, - {V4L2_PIX_FMT_SPCA561, 176, 144, 2}, + {V4L2_PIX_FMT_SGBRG8, 160, 120, 3}, + {V4L2_PIX_FMT_SGBRG8, 176, 144, 2}, {V4L2_PIX_FMT_SPCA561, 320, 240, 1}, {V4L2_PIX_FMT_SPCA561, 352, 288, 0}, }; @@ -808,7 +808,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len); } else { - /*fixme: which format?*/ + /* raw bayer (with a header, which we skip) */ data += 20; len -= 20; gspca_frame_add(gspca_dev, FIRST_PACKET, diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 43a6c81a53ef..577f15634791 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -310,6 +310,7 @@ struct v4l2_pix_format /* see http://www.siliconimaging.com/RGB%20Bayer.htm */ #define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B','A','8','1') /* 8 BGBG.. GRGR.. */ +#define V4L2_PIX_FMT_SGBRG8 v4l2_fourcc('G','B','R','G') /* 8 GBGB.. RGRG.. */ #define V4L2_PIX_FMT_SBGGR16 v4l2_fourcc('B','Y','R','2') /* 16 BGBG.. GRGR.. */ /* compressed formats */ -- cgit v1.2.3 From ab8f12cf8e3d33adec382585f9f60f8686a372c3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 4 Jul 2008 18:29:32 -0300 Subject: V4L/DVB (8197): gspca: pac207 frames no more decoded in the subdriver. videodev2: New pixfmt pac207: Remove the specific decoding. main: get_buff_size operation added for the subdriver. Signed-off-by: Hans de Goede Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 7 +- drivers/media/video/gspca/gspca.h | 2 + drivers/media/video/gspca/pac207.c | 353 +++++-------------------------------- include/linux/videodev2.h | 3 +- 4 files changed, 53 insertions(+), 312 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index f6d216316b11..708270a46e1f 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -321,6 +321,7 @@ static int gspca_is_compressed(__u32 format) case V4L2_PIX_FMT_MJPEG: case V4L2_PIX_FMT_JPEG: case V4L2_PIX_FMT_SPCA561: + case V4L2_PIX_FMT_PAC207: return 1; } return 0; @@ -380,7 +381,8 @@ static __u32 get_v4l2_depth(__u32 pixfmt) case V4L2_PIX_FMT_JPEG: case V4L2_PIX_FMT_SBGGR8: /* 'BA81' Bayer */ case V4L2_PIX_FMT_SN9C10X: /* 'S910' SN9C10x compression */ - case V4L2_PIX_FMT_SPCA561: /* 'S561' compressed BGGR bayer */ + case V4L2_PIX_FMT_SPCA561: /* 'S561' compressed GBRG bayer */ + case V4L2_PIX_FMT_PAC207: /* 'P207' compressed BGGR bayer */ return 8; } PDEBUG(D_ERR|D_CONF, "Unknown pixel format %c%c%c%c", @@ -395,6 +397,9 @@ static int gspca_get_buff_size(struct gspca_dev *gspca_dev, int mode) { unsigned int size; + if (gspca_dev->sd_desc->get_buff_size) + return gspca_dev->sd_desc->get_buff_size(gspca_dev, mode); + size = gspca_dev->cam.cam_mode[mode].width * gspca_dev->cam.cam_mode[mode].height * get_v4l2_depth(gspca_dev->cam.cam_mode[mode].pixfmt) / 8; diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 0d23e0a90c77..05e8ee06071b 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -80,6 +80,7 @@ typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev, struct gspca_frame *frame, __u8 *data, int len); +typedef int (*cam_get_buff_size_op) (struct gspca_dev *gspca_dev, int mode); struct ctrl { struct v4l2_queryctrl qctrl; @@ -106,6 +107,7 @@ struct sd_desc { cam_jpg_op get_jcomp; cam_jpg_op set_jcomp; cam_qmnu_op querymenu; + cam_get_buff_size_op get_buff_size; /* optional */ }; /* packet types when moving from iso buf to frame buf */ diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c index 7e067456e62b..0135ba599e81 100644 --- a/drivers/media/video/gspca/pac207.c +++ b/drivers/media/video/gspca/pac207.c @@ -58,34 +58,10 @@ MODULE_LICENSE("GPL"); before doing any other adjustments */ #define PAC207_AUTOGAIN_IGNORE_FRAMES 3 -enum pac207_line_state { - LINE_HEADER1, - LINE_HEADER2, - LINE_UNCOMPRESSED, - LINE_COMPRESSED, -}; - -struct pac207_decoder_state { - /* generic state */ - u16 line_read; - u16 line_marker; - u8 line_state; - u8 header_read; - /* compression state */ - u16 processed_bytes; - u8 remaining_bits; - s8 no_remaining_bits; - u8 get_abs; - u8 discard_byte; - u8 line_decode_buf[352]; -}; - /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - struct pac207_decoder_state decoder_state; - u8 mode; u8 brightness; @@ -94,6 +70,7 @@ struct sd { u8 gain; u8 sof_read; + u8 header_read; u8 autogain_ignore_frames; atomic_t avg_lum; @@ -173,8 +150,8 @@ static struct ctrl sd_ctrls[] = { }; static struct cam_mode sif_mode[] = { - {V4L2_PIX_FMT_SBGGR8, 176, 144, 1}, - {V4L2_PIX_FMT_SBGGR8, 352, 288, 0}, + {V4L2_PIX_FMT_PAC207, 176, 144, 1}, + {V4L2_PIX_FMT_PAC207, 352, 288, 0}, }; static const __u8 pac207_sensor_init[][8] = { @@ -361,68 +338,17 @@ static void sd_close(struct gspca_dev *gspca_dev) { } -/* -- convert pixart frames to Bayer -- */ -/* Sonix decompressor struct B.S.(2004) */ -static struct { - u8 is_abs; - u8 len; - s8 val; -} table[256]; - -void init_pixart_decoder(void) +static int sd_get_buff_size_op(struct gspca_dev *gspca_dev, int mode) { - int i, is_abs, val, len; - - for (i = 0; i < 256; i++) { - is_abs = 0; - val = 0; - len = 0; - if ((i & 0xC0) == 0) { - /* code 00 */ - val = 0; - len = 2; - } else if ((i & 0xC0) == 0x40) { - /* code 01 */ - val = -5; - len = 2; - } else if ((i & 0xC0) == 0x80) { - /* code 10 */ - val = 5; - len = 2; - } else if ((i & 0xF0) == 0xC0) { - /* code 1100 */ - val = -10; - len = 4; - } else if ((i & 0xF0) == 0xD0) { - /* code 1101 */ - val = 10; - len = 4; - } else if ((i & 0xF8) == 0xE0) { - /* code 11100 */ - val = -15; - len = 5; - } else if ((i & 0xF8) == 0xE8) { - /* code 11101 */ - val = 15; - len = 5; - } else if ((i & 0xFC) == 0xF0) { - /* code 111100 */ - val = -20; - len = 6; - } else if ((i & 0xFC) == 0xF4) { - /* code 111101 */ - val = 20; - len = 6; - } else if ((i & 0xF8) == 0xF8) { - /* code 11111xxxxxx */ - is_abs = 1; - val = 0; - len = 5; - } - table[i].is_abs = is_abs; - table[i].val = val; - table[i].len = len; + switch (gspca_dev->cam.cam_mode[mode].width) { + case 176: /* 176x144 */ + /* uncompressed, add 2 bytes / line for line header */ + return (176 + 2) * 144; + case 352: /* 352x288 */ + /* compressed */ + return 352 * 288 / 2; } + return -EIO; /* should never happen */ } /* auto gain and exposure algorithm based on the knee algorithm described here: @@ -517,245 +443,52 @@ static unsigned char *pac207_find_sof(struct gspca_dev *gspca_dev, return NULL; } -static int pac207_decompress_row(struct gspca_dev *gspca_dev, - struct gspca_frame *f, - __u8 *cdata, - int len) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct pac207_decoder_state *decoder_state = &sd->decoder_state; - unsigned char *outp = decoder_state->line_decode_buf + - decoder_state->line_read; - int val, bitlen, bitpos = -decoder_state->no_remaining_bits; - u8 code; - - /* first two pixels are stored as raw 8-bit */ - while (decoder_state->line_read < 2) { - *outp++ = *cdata++; - decoder_state->line_read++; - len--; - if (len == 0) - return 0; - } - - while (decoder_state->line_read < gspca_dev->width) { - if (bitpos < 0) { - code = decoder_state->remaining_bits << (8 + bitpos) | - cdata[0] >> -bitpos; - } else { - u8 *addr = cdata + bitpos / 8; - code = addr[0] << (bitpos & 7) | - addr[1] >> (8 - (bitpos & 7)); - } - - bitlen = decoder_state->get_abs ? - 6 : table[code].len; - - /* Stop decompressing if we're out of input data */ - if ((bitpos + bitlen) > (len * 8)) - break; - - if (decoder_state->get_abs) { - *outp++ = code & 0xFC; - decoder_state->line_read++; - decoder_state->get_abs = 0; - } else { - if (table[code].is_abs) { - decoder_state->get_abs = 1; - } else { - /* relative to left pixel */ - val = outp[-2] + - table[code].val; - if (val > 0xff) - val = 0xff; - else if (val < 0) - val = 0; - *outp++ = val; - decoder_state->line_read++; - } - } - bitpos += bitlen; - } - - if (decoder_state->line_read == gspca_dev->width) { - int compressed_line_len; - - gspca_frame_add(gspca_dev, INTER_PACKET, f, - decoder_state->line_decode_buf, - gspca_dev->width); - - /* completely decompressed line, round pos to nearest word */ - compressed_line_len = ((decoder_state->processed_bytes * 8 + - bitpos + 15) / 16) * 2; - - len -= compressed_line_len - decoder_state->processed_bytes; - if (len < 0) { - decoder_state->discard_byte = 1; - len = 0; - } - } else { - decoder_state->processed_bytes += len; - decoder_state->remaining_bits = cdata[bitpos/8]; - decoder_state->no_remaining_bits = (8 - bitpos) & 7; - len = 0; - } - - return len; -} - -static void pac207_decode_line_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct pac207_decoder_state *decoder_state = &sd->decoder_state; - - decoder_state->line_read = 0; - decoder_state->line_state = LINE_HEADER1; - decoder_state->processed_bytes = 0; - decoder_state->no_remaining_bits = 0; - decoder_state->get_abs = 0; -} - -static void pac207_decode_frame_init(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct pac207_decoder_state *decoder_state = &sd->decoder_state; - - decoder_state->header_read = 0; - decoder_state->discard_byte = 0; - - pac207_decode_line_init(gspca_dev); -} - -static int pac207_decode_frame_data(struct gspca_dev *gspca_dev, - struct gspca_frame *f, unsigned char *data, int len) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct pac207_decoder_state *decoder_state = &sd->decoder_state; - int needed = 0; - - /* first 11 bytes after sof marker: frame header */ - if (decoder_state->header_read < 11) { - /* get average lumination from frame header (byte 5) */ - if (decoder_state->header_read < 5) { - needed = 5 - decoder_state->header_read; - if (len >= needed) - atomic_set(&sd->avg_lum, data[needed-1]); - } - /* skip the rest of the header */ - needed = 11 - decoder_state->header_read; - if (len <= needed) { - decoder_state->header_read += len; - return 0; - } - data += needed; - len -= needed; - decoder_state->header_read = 11; - } - - while (len) { - if (decoder_state->discard_byte) { - data++; - len--; - decoder_state->discard_byte = 0; - continue; - } - - switch (decoder_state->line_state) { - case LINE_HEADER1: - decoder_state->line_marker = data[0] << 8; - decoder_state->line_state = LINE_HEADER2; - needed = 1; - break; - case LINE_HEADER2: - decoder_state->line_marker |= data[0]; - switch (decoder_state->line_marker) { - case 0x0ff0: - decoder_state->line_state = LINE_UNCOMPRESSED; - break; - case 0x1ee1: - decoder_state->line_state = LINE_COMPRESSED; - break; - default: - PDEBUG(D_STREAM, - "Error unknown line-header %04X", - (int) decoder_state->line_marker); - gspca_dev->last_packet_type = DISCARD_PACKET; - return 0; - } - needed = 1; - break; - case LINE_UNCOMPRESSED: - needed = gspca_dev->width - decoder_state->line_read; - if (needed > len) - needed = len; - gspca_frame_add(gspca_dev, INTER_PACKET, f, data, - needed); - decoder_state->line_read += needed; - break; - case LINE_COMPRESSED: - needed = len - - pac207_decompress_row(gspca_dev, f, data, len); - break; - } - - data += needed; - len -= needed; - - if (decoder_state->line_read == gspca_dev->width) { - if ((f->data_end - f->data) == - (gspca_dev->width * gspca_dev->height)) { - /* eureka we've got a frame */ - return 1; - } - pac207_decode_line_init(gspca_dev); - } - } - - return 0; -} - static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, __u8 *data, int len) { + struct sd *sd = (struct sd *) gspca_dev; unsigned char *sof; - int n; sof = pac207_find_sof(gspca_dev, data, len); - if (sof) { + int n; + /* finish decoding current frame */ - if (gspca_dev->last_packet_type == INTER_PACKET) { - n = sof - data; - if (n > sizeof(pac207_sof_marker)) - n -= sizeof(pac207_sof_marker); - else - n = 0; - n = pac207_decode_frame_data(gspca_dev, frame, - data, n); - if (n) - frame = gspca_frame_add(gspca_dev, - LAST_PACKET, - frame, - NULL, - 0); - else - PDEBUG(D_STREAM, "Incomplete frame"); - } - pac207_decode_frame_init(gspca_dev); + n = sof - data; + if (n > sizeof pac207_sof_marker) + n -= sizeof pac207_sof_marker; + else + n = 0; + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, n); + sd->header_read = 0; gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0); len -= sof - data; data = sof; } + if (sd->header_read < 11) { + int needed; - if (gspca_dev->last_packet_type == DISCARD_PACKET) - return; + /* get average lumination from frame header (byte 5) */ + if (sd->header_read < 5) { + needed = 5 - sd->header_read; + if (len >= needed) + atomic_set(&sd->avg_lum, data[needed - 1]); + } + /* skip the rest of the header */ + needed = 11 - sd->header_read; + if (len <= needed) { + sd->header_read += len; + return; + } + data += needed; + len -= needed; + sd->header_read = 11; + } - n = pac207_decode_frame_data(gspca_dev, frame, data, len); - if (n) - gspca_frame_add(gspca_dev, LAST_PACKET, - frame, NULL, 0); + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); } static void setbrightness(struct gspca_dev *gspca_dev) @@ -891,6 +624,7 @@ static const struct sd_desc sd_desc = { .close = sd_close, .dq_callback = pac207_do_auto_gain, .pkt_scan = sd_pkt_scan, + .get_buff_size = sd_get_buff_size_op, }; /* -- module initialisation -- */ @@ -927,7 +661,6 @@ static struct usb_driver sd_driver = { /* -- module insert / remove -- */ static int __init sd_mod_init(void) { - init_pixart_decoder(); if (usb_register(&sd_driver) < 0) return -1; PDEBUG(D_PROBE, "v%s registered", version); diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 577f15634791..2e66a95e8d32 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -326,7 +326,8 @@ struct v4l2_pix_format #define V4L2_PIX_FMT_PWC2 v4l2_fourcc('P','W','C','2') /* pwc newer webcam */ #define V4L2_PIX_FMT_ET61X251 v4l2_fourcc('E','6','2','5') /* ET61X251 compression */ #define V4L2_PIX_FMT_SPCA501 v4l2_fourcc('S','5','0','1') /* YUYV per line */ -#define V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S','5','6','1') /* compressed BGGR bayer */ +#define V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S','5','6','1') /* compressed GBRG bayer */ +#define V4L2_PIX_FMT_PAC207 v4l2_fourcc('P','2','0','7') /* compressed BGGR bayer */ /* * F O R M A T E N U M E R A T I O N -- cgit v1.2.3 From be99af6679174e5d0e9f36fc8c18318a8ce34bca Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 10 Jul 2008 11:04:10 -0300 Subject: V4L/DVB (8245): ovcamchip: Delete stray I2C bus ID I2C_HW_SMBUS_OVFX2 is referenced in ovcamchip_core.c, but no bus uses this driver ID, so we can remove the reference. As far as I can see, the Cypress FX2 webcam is handled by a different driver (dvb-usb). Signed-off-by: Jean Delvare Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/ovcamchip/ovcamchip_core.c | 1 - include/linux/i2c-id.h | 1 - 2 files changed, 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/video/ovcamchip/ovcamchip_core.c b/drivers/media/video/ovcamchip/ovcamchip_core.c index 8063e33f1c85..065c2454113e 100644 --- a/drivers/media/video/ovcamchip/ovcamchip_core.c +++ b/drivers/media/video/ovcamchip/ovcamchip_core.c @@ -297,7 +297,6 @@ static int ovcamchip_attach(struct i2c_adapter *adap) switch (adap->id) { case I2C_HW_SMBUS_OV511: case I2C_HW_SMBUS_OV518: - case I2C_HW_SMBUS_OVFX2: case I2C_HW_SMBUS_W9968CF: PDEBUG(1, "Adapter ID 0x%06x accepted", adap->id); break; diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h index ef13b7c66df3..9083847157a3 100644 --- a/include/linux/i2c-id.h +++ b/include/linux/i2c-id.h @@ -158,7 +158,6 @@ #define I2C_HW_SMBUS_W9968CF 0x04000d #define I2C_HW_SMBUS_OV511 0x04000e /* OV511(+) USB 1.1 webcam ICs */ #define I2C_HW_SMBUS_OV518 0x04000f /* OV518(+) USB 1.1 webcam ICs */ -#define I2C_HW_SMBUS_OVFX2 0x040011 /* Cypress/OmniVision FX2 webcam */ #define I2C_HW_SMBUS_CAFE 0x040012 /* Marvell 88ALP01 "CAFE" cam */ #define I2C_HW_SMBUS_ALI1563 0x040013 -- cgit v1.2.3 From 5a367dfb739831d54caf226ce0bc4c75ef264d8d Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 10 Jul 2008 11:23:37 -0300 Subject: V4L/DVB (8246): tvaudio: Stop I2C driver ID abuse The tvaudio driver is using "official" I2C device IDs for internal purpose. There must be some historical reason behind this but anyway, it shouldn't do that. As the stored values are never used, the easiest way to fix the problem is simply to remove them altogether. Signed-off-by: Jean Delvare Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/tvaudio.c | 13 ------------- include/linux/i2c-id.h | 7 ------- 2 files changed, 20 deletions(-) (limited to 'include/linux') diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index c77914d99d15..463680b13289 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -69,7 +69,6 @@ typedef struct AUDIOCMD { /* chip description */ struct CHIPDESC { char *name; /* chip name */ - int id; /* ID */ int addr_lo, addr_hi; /* i2c address range */ int registers; /* # of registers */ @@ -1256,7 +1255,6 @@ module_param(ta8874z, int, 0444); static struct CHIPDESC chiplist[] = { { .name = "tda9840", - .id = I2C_DRIVERID_TDA9840, .insmodopt = &tda9840, .addr_lo = I2C_ADDR_TDA9840 >> 1, .addr_hi = I2C_ADDR_TDA9840 >> 1, @@ -1272,7 +1270,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tda9873h", - .id = I2C_DRIVERID_TDA9873, .checkit = tda9873_checkit, .insmodopt = &tda9873, .addr_lo = I2C_ADDR_TDA985x_L >> 1, @@ -1293,7 +1290,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tda9874h/a", - .id = I2C_DRIVERID_TDA9874, .checkit = tda9874a_checkit, .initialize = tda9874a_initialize, .insmodopt = &tda9874a, @@ -1306,7 +1302,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tda9850", - .id = I2C_DRIVERID_TDA9850, .insmodopt = &tda9850, .addr_lo = I2C_ADDR_TDA985x_L >> 1, .addr_hi = I2C_ADDR_TDA985x_H >> 1, @@ -1319,7 +1314,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tda9855", - .id = I2C_DRIVERID_TDA9855, .insmodopt = &tda9855, .addr_lo = I2C_ADDR_TDA985x_L >> 1, .addr_hi = I2C_ADDR_TDA985x_H >> 1, @@ -1344,7 +1338,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tea6300", - .id = I2C_DRIVERID_TEA6300, .insmodopt = &tea6300, .addr_lo = I2C_ADDR_TEA6300 >> 1, .addr_hi = I2C_ADDR_TEA6300 >> 1, @@ -1365,7 +1358,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tea6320", - .id = I2C_DRIVERID_TEA6300, .initialize = tea6320_initialize, .insmodopt = &tea6320, .addr_lo = I2C_ADDR_TEA6300 >> 1, @@ -1387,7 +1379,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tea6420", - .id = I2C_DRIVERID_TEA6420, .insmodopt = &tea6420, .addr_lo = I2C_ADDR_TEA6420 >> 1, .addr_hi = I2C_ADDR_TEA6420 >> 1, @@ -1400,7 +1391,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tda8425", - .id = I2C_DRIVERID_TDA8425, .insmodopt = &tda8425, .addr_lo = I2C_ADDR_TDA8425 >> 1, .addr_hi = I2C_ADDR_TDA8425 >> 1, @@ -1424,7 +1414,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "pic16c54 (PV951)", - .id = I2C_DRIVERID_PIC16C54_PV9, .insmodopt = &pic16c54, .addr_lo = I2C_ADDR_PIC16C54 >> 1, .addr_hi = I2C_ADDR_PIC16C54>> 1, @@ -1440,8 +1429,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "ta8874z", - .id = -1, - /*.id = I2C_DRIVERID_TA8874Z, */ .checkit = ta8874z_checkit, .insmodopt = &ta8874z, .addr_lo = I2C_ADDR_TDA9840 >> 1, diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h index 9083847157a3..4862398e05bf 100644 --- a/include/linux/i2c-id.h +++ b/include/linux/i2c-id.h @@ -33,15 +33,11 @@ #define I2C_DRIVERID_MSP3400 1 #define I2C_DRIVERID_TUNER 2 -#define I2C_DRIVERID_TDA8425 4 /* stereo sound processor */ #define I2C_DRIVERID_TEA6420 5 /* audio matrix switch */ #define I2C_DRIVERID_TEA6415C 6 /* video matrix switch */ #define I2C_DRIVERID_TDA9840 7 /* stereo sound processor */ #define I2C_DRIVERID_SAA7111A 8 /* video input processor */ #define I2C_DRIVERID_SAA7185B 13 /* video encoder */ -#define I2C_DRIVERID_TEA6300 18 /* audio mixer */ -#define I2C_DRIVERID_TDA9850 20 /* audio mixer */ -#define I2C_DRIVERID_TDA9855 21 /* audio mixer */ #define I2C_DRIVERID_SAA7110 22 /* video decoder */ #define I2C_DRIVERID_MGATVO 23 /* Matrox TVOut */ #define I2C_DRIVERID_SAA5249 24 /* SAA5249 and compatibles */ @@ -50,9 +46,7 @@ #define I2C_DRIVERID_TDA7432 27 /* Stereo sound processor */ #define I2C_DRIVERID_TVMIXER 28 /* Mixer driver for tv cards */ #define I2C_DRIVERID_TVAUDIO 29 /* Generic TV sound driver */ -#define I2C_DRIVERID_TDA9873 31 /* TV sound decoder chip */ #define I2C_DRIVERID_TDA9875 32 /* TV sound decoder chip */ -#define I2C_DRIVERID_PIC16C54_PV9 33 /* Audio mux/ir receiver */ #define I2C_DRIVERID_BT819 40 /* video decoder */ #define I2C_DRIVERID_BT856 41 /* video encoder */ #define I2C_DRIVERID_VPX3220 42 /* video decoder+vbi/vtxt */ @@ -63,7 +57,6 @@ #define I2C_DRIVERID_INDYCAM 58 /* SGI IndyCam */ #define I2C_DRIVERID_OVCAMCHIP 61 /* OmniVision CMOS image sens. */ #define I2C_DRIVERID_MAX6900 63 /* MAX6900 real-time clock */ -#define I2C_DRIVERID_TDA9874 66 /* TV sound decoder */ #define I2C_DRIVERID_SAA6752HS 67 /* MPEG2 encoder */ #define I2C_DRIVERID_TVEEPROM 68 /* TV EEPROM */ #define I2C_DRIVERID_WM8775 69 /* wm8775 audio processor */ -- cgit v1.2.3 From 6f2384c4bdd4be3dc1e5d22ed5e6f0c3076fda60 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Fri, 20 Jun 2008 11:02:19 +0200 Subject: mfd: asic3 gpiolib support ASIC3 is, among other things, a GPIO extender. We should thus have it supporting the current gpiolib API. Signed-off-by: Samuel Ortiz Signed-off-by: Andrew Morton --- drivers/mfd/asic3.c | 224 +++++++++++++++++++++++++++++++--------------- include/linux/mfd/asic3.h | 24 +++-- 2 files changed, 164 insertions(+), 84 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index ef8a492766a7..c70e7a5a5a90 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -9,7 +9,7 @@ * * Copyright 2001 Compaq Computer Corporation. * Copyright 2004-2005 Phil Blundell - * Copyright 2007 OpenedHand Ltd. + * Copyright 2007-2008 OpenedHand Ltd. * * Authors: Phil Blundell , * Samuel Ortiz @@ -19,12 +19,26 @@ #include #include #include +#include #include #include #include #include +struct asic3 { + void __iomem *mapping; + unsigned int bus_shift; + unsigned int irq_nr; + unsigned int irq_base; + spinlock_t lock; + u16 irq_bothedge[4]; + struct gpio_chip gpio; + struct device *dev; +}; + +static int asic3_gpio_get(struct gpio_chip *chip, unsigned offset); + static inline void asic3_write_register(struct asic3 *asic, unsigned int reg, u32 value) { @@ -251,7 +265,7 @@ static int asic3_gpio_irq_type(unsigned int irq, unsigned int type) edge &= ~bit; } else if (type == IRQT_BOTHEDGE) { trigger |= bit; - if (asic3_gpio_get_value(asic, irq - asic->irq_base)) + if (asic3_gpio_get(&asic->gpio, irq - asic->irq_base)) edge &= ~bit; else edge |= bit; @@ -350,6 +364,107 @@ static void asic3_irq_remove(struct platform_device *pdev) } /* GPIOs */ +static int asic3_gpio_direction(struct gpio_chip *chip, + unsigned offset, int out) +{ + u32 mask = ASIC3_GPIO_TO_MASK(offset), out_reg; + unsigned int gpio_base; + unsigned long flags; + struct asic3 *asic; + + asic = container_of(chip, struct asic3, gpio); + gpio_base = ASIC3_GPIO_TO_BASE(offset); + + if (gpio_base > ASIC3_GPIO_D_Base) { + printk(KERN_ERR "Invalid base (0x%x) for gpio %d\n", + gpio_base, offset); + return -EINVAL; + } + + spin_lock_irqsave(&asic->lock, flags); + + out_reg = asic3_read_register(asic, gpio_base + ASIC3_GPIO_Direction); + + /* Input is 0, Output is 1 */ + if (out) + out_reg |= mask; + else + out_reg &= ~mask; + + asic3_write_register(asic, gpio_base + ASIC3_GPIO_Direction, out_reg); + + spin_unlock_irqrestore(&asic->lock, flags); + + return 0; + +} + +static int asic3_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + return asic3_gpio_direction(chip, offset, 0); +} + +static int asic3_gpio_direction_output(struct gpio_chip *chip, + unsigned offset, int value) +{ + return asic3_gpio_direction(chip, offset, 1); +} + +static int asic3_gpio_get(struct gpio_chip *chip, + unsigned offset) +{ + unsigned int gpio_base; + u32 mask = ASIC3_GPIO_TO_MASK(offset); + struct asic3 *asic; + + asic = container_of(chip, struct asic3, gpio); + gpio_base = ASIC3_GPIO_TO_BASE(offset); + + if (gpio_base > ASIC3_GPIO_D_Base) { + printk(KERN_ERR "Invalid base (0x%x) for gpio %d\n", + gpio_base, offset); + return -EINVAL; + } + + return asic3_read_register(asic, gpio_base + ASIC3_GPIO_Status) & mask; +} + +static void asic3_gpio_set(struct gpio_chip *chip, + unsigned offset, int value) +{ + u32 mask, out_reg; + unsigned int gpio_base; + unsigned long flags; + struct asic3 *asic; + + asic = container_of(chip, struct asic3, gpio); + gpio_base = ASIC3_GPIO_TO_BASE(offset); + + if (gpio_base > ASIC3_GPIO_D_Base) { + printk(KERN_ERR "Invalid base (0x%x) for gpio %d\n", + gpio_base, offset); + return; + } + + mask = ASIC3_GPIO_TO_MASK(offset); + + spin_lock_irqsave(&asic->lock, flags); + + out_reg = asic3_read_register(asic, gpio_base + ASIC3_GPIO_Out); + + if (value) + out_reg |= mask; + else + out_reg &= ~mask; + + asic3_write_register(asic, gpio_base + ASIC3_GPIO_Out, out_reg); + + spin_unlock_irqrestore(&asic->lock, flags); + + return; +} + static inline u32 asic3_get_gpio(struct asic3 *asic, unsigned int base, unsigned int function) { @@ -368,15 +483,6 @@ static void asic3_set_gpio(struct asic3 *asic, unsigned int base, spin_unlock_irqrestore(&asic->lock, flags); } -#define asic3_get_gpio_a(asic, fn) \ - asic3_get_gpio(asic, ASIC3_GPIO_A_Base, ASIC3_GPIO_##fn) -#define asic3_get_gpio_b(asic, fn) \ - asic3_get_gpio(asic, ASIC3_GPIO_B_Base, ASIC3_GPIO_##fn) -#define asic3_get_gpio_c(asic, fn) \ - asic3_get_gpio(asic, ASIC3_GPIO_C_Base, ASIC3_GPIO_##fn) -#define asic3_get_gpio_d(asic, fn) \ - asic3_get_gpio(asic, ASIC3_GPIO_D_Base, ASIC3_GPIO_##fn) - #define asic3_set_gpio_a(asic, fn, bits, val) \ asic3_set_gpio(asic, ASIC3_GPIO_A_Base, ASIC3_GPIO_##fn, bits, val) #define asic3_set_gpio_b(asic, fn, bits, val) \ @@ -394,54 +500,6 @@ static void asic3_set_gpio(struct asic3 *asic, unsigned int base, asic3_set_gpio_d((asic), fn, (bits), (pdata)->gpio_d.field); \ } while (0) -int asic3_gpio_get_value(struct asic3 *asic, unsigned gpio) -{ - u32 mask = ASIC3_GPIO_bit(gpio); - - switch (gpio >> 4) { - case ASIC3_GPIO_BANK_A: - return asic3_get_gpio_a(asic, Status) & mask; - case ASIC3_GPIO_BANK_B: - return asic3_get_gpio_b(asic, Status) & mask; - case ASIC3_GPIO_BANK_C: - return asic3_get_gpio_c(asic, Status) & mask; - case ASIC3_GPIO_BANK_D: - return asic3_get_gpio_d(asic, Status) & mask; - default: - printk(KERN_ERR "%s: invalid GPIO value 0x%x", - __func__, gpio); - return -EINVAL; - } -} -EXPORT_SYMBOL(asic3_gpio_get_value); - -void asic3_gpio_set_value(struct asic3 *asic, unsigned gpio, int val) -{ - u32 mask = ASIC3_GPIO_bit(gpio); - u32 bitval = 0; - if (val) - bitval = mask; - - switch (gpio >> 4) { - case ASIC3_GPIO_BANK_A: - asic3_set_gpio_a(asic, Out, mask, bitval); - return; - case ASIC3_GPIO_BANK_B: - asic3_set_gpio_b(asic, Out, mask, bitval); - return; - case ASIC3_GPIO_BANK_C: - asic3_set_gpio_c(asic, Out, mask, bitval); - return; - case ASIC3_GPIO_BANK_D: - asic3_set_gpio_d(asic, Out, mask, bitval); - return; - default: - printk(KERN_ERR "%s: invalid GPIO value 0x%x", - __func__, gpio); - return; - } -} -EXPORT_SYMBOL(asic3_gpio_set_value); static int asic3_gpio_probe(struct platform_device *pdev) { @@ -472,12 +530,14 @@ static int asic3_gpio_probe(struct platform_device *pdev) alt_function); } - return 0; + return gpiochip_add(&asic->gpio); } -static void asic3_gpio_remove(struct platform_device *pdev) +static int asic3_gpio_remove(struct platform_device *pdev) { - return; + struct asic3 *asic = platform_get_drvdata(pdev); + + return gpiochip_remove(&asic->gpio); } @@ -488,11 +548,13 @@ static int asic3_probe(struct platform_device *pdev) struct asic3 *asic; struct resource *mem; unsigned long clksel; - int ret; + int ret = 0; asic = kzalloc(sizeof(struct asic3), GFP_KERNEL); - if (!asic) + if (asic == NULL) { + printk(KERN_ERR "kzalloc failed\n"); return -ENOMEM; + } spin_lock_init(&asic->lock); platform_set_drvdata(pdev, asic); @@ -502,14 +564,15 @@ static int asic3_probe(struct platform_device *pdev) if (!mem) { ret = -ENOMEM; printk(KERN_ERR "asic3: no MEM resource\n"); - goto err_out_1; + goto out_free; } + asic->mapping = ioremap(mem->start, PAGE_SIZE); if (!asic->mapping) { ret = -ENOMEM; printk(KERN_ERR "asic3: couldn't ioremap\n"); - goto err_out_1; + goto out_free; } asic->irq_base = pdata->irq_base; @@ -525,9 +588,21 @@ static int asic3_probe(struct platform_device *pdev) ret = asic3_irq_probe(pdev); if (ret < 0) { printk(KERN_ERR "asic3: couldn't probe IRQs\n"); - goto err_out_2; + goto out_unmap; + } + + asic->gpio.base = pdata->gpio_base; + asic->gpio.ngpio = ASIC3_NUM_GPIOS; + asic->gpio.get = asic3_gpio_get; + asic->gpio.set = asic3_gpio_set; + asic->gpio.direction_input = asic3_gpio_direction_input; + asic->gpio.direction_output = asic3_gpio_direction_output; + + ret = asic3_gpio_probe(pdev); + if (ret < 0) { + printk(KERN_ERR "GPIO probe failed\n"); + goto out_irq; } - asic3_gpio_probe(pdev); if (pdata->children) { int i; @@ -541,9 +616,13 @@ static int asic3_probe(struct platform_device *pdev) return 0; - err_out_2: + out_irq: + asic3_irq_remove(pdev); + + out_unmap: iounmap(asic->mapping); - err_out_1: + + out_free: kfree(asic); return ret; @@ -551,9 +630,12 @@ static int asic3_probe(struct platform_device *pdev) static int asic3_remove(struct platform_device *pdev) { + int ret; struct asic3 *asic = platform_get_drvdata(pdev); - asic3_gpio_remove(pdev); + ret = asic3_gpio_remove(pdev); + if (ret < 0) + return ret; asic3_irq_remove(pdev); asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL), 0); diff --git a/include/linux/mfd/asic3.h b/include/linux/mfd/asic3.h index 4ab2162db13b..06ef8165f406 100644 --- a/include/linux/mfd/asic3.h +++ b/include/linux/mfd/asic3.h @@ -16,16 +16,6 @@ #include -struct asic3 { - void __iomem *mapping; - unsigned int bus_shift; - unsigned int irq_nr; - unsigned int irq_base; - spinlock_t lock; - u16 irq_bothedge[4]; - struct device *dev; -}; - struct asic3_platform_data { struct { u32 dir; @@ -41,18 +31,19 @@ struct asic3_platform_data { unsigned int irq_base; + unsigned int gpio_base; + struct platform_device **children; unsigned int n_children; }; -int asic3_gpio_get_value(struct asic3 *asic, unsigned gpio); -void asic3_gpio_set_value(struct asic3 *asic, unsigned gpio, int val); - #define ASIC3_NUM_GPIO_BANKS 4 #define ASIC3_GPIOS_PER_BANK 16 #define ASIC3_NUM_GPIOS 64 #define ASIC3_NR_IRQS ASIC3_NUM_GPIOS + 6 +#define ASIC3_TO_GPIO(gpio) (NR_BUILTIN_GPIO + (gpio)) + #define ASIC3_GPIO_BANK_A 0 #define ASIC3_GPIO_BANK_B 1 #define ASIC3_GPIO_BANK_C 2 @@ -73,6 +64,13 @@ void asic3_gpio_set_value(struct asic3 *asic, unsigned gpio, int val); #define ASIC3_GPIO_C_Base 0x0200 #define ASIC3_GPIO_D_Base 0x0300 +#define ASIC3_GPIO_TO_BANK(gpio) ((gpio) >> 4) +#define ASIC3_GPIO_TO_BIT(gpio) ((gpio) - \ + (ASIC3_GPIOS_PER_BANK * ((gpio) >> 4))) +#define ASIC3_GPIO_TO_MASK(gpio) (1 << ASIC3_GPIO_TO_BIT(gpio)) +#define ASIC3_GPIO_TO_BASE(gpio) (ASIC3_GPIO_A_Base + (((gpio) >> 4) * 0x0100)) +#define ASIC3_BANK_TO_BASE(bank) (ASIC3_GPIO_A_Base + ((bank) * 0x100)) + #define ASIC3_GPIO_Mask 0x00 /* R/W 0:don't mask */ #define ASIC3_GPIO_Direction 0x04 /* R/W 0:input */ #define ASIC3_GPIO_Out 0x08 /* R/W 0:output low */ -- cgit v1.2.3 From 1effe5bc6cfbac4506d7944d68dadbd29ad62645 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Fri, 20 Jun 2008 11:07:39 +0200 Subject: mfd: asic3 children platform data removal Platform devices should be dynamically allocated, and each supported device should have its own platform data. For now we just remove this buggy code. Signed-off-by: Samuel Ortiz Signed-off-by: Andrew Morton --- drivers/mfd/asic3.c | 8 -------- include/linux/mfd/asic3.h | 3 --- 2 files changed, 11 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index c70e7a5a5a90..dfee0a2ba167 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -604,14 +604,6 @@ static int asic3_probe(struct platform_device *pdev) goto out_irq; } - if (pdata->children) { - int i; - for (i = 0; i < pdata->n_children; i++) { - pdata->children[i]->dev.parent = &pdev->dev; - platform_device_register(pdata->children[i]); - } - } - printk(KERN_INFO "ASIC3 Core driver\n"); return 0; diff --git a/include/linux/mfd/asic3.h b/include/linux/mfd/asic3.h index 06ef8165f406..b1c365800ab5 100644 --- a/include/linux/mfd/asic3.h +++ b/include/linux/mfd/asic3.h @@ -32,9 +32,6 @@ struct asic3_platform_data { unsigned int irq_base; unsigned int gpio_base; - - struct platform_device **children; - unsigned int n_children; }; #define ASIC3_NUM_GPIO_BANKS 4 -- cgit v1.2.3 From 3b26bf17226f66bfd6cd4e36ac81f83fe994043a Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Fri, 20 Jun 2008 11:09:51 +0200 Subject: mfd: New asic3 gpio configuration code The ASIC3 GPIO configuration code is a bit obscure and hardly readable. This patch changes it so that it is now more readable and understandable, by being more explicit. Signed-off-by: Samuel Ortiz Signed-off-by: Andrew Morton --- drivers/mfd/asic3.c | 99 ++++++++++++++++++++--------------------------- include/linux/mfd/asic3.h | 34 +++++++++++----- 2 files changed, 67 insertions(+), 66 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index dfee0a2ba167..36b46ded1bff 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -465,69 +465,54 @@ static void asic3_gpio_set(struct gpio_chip *chip, return; } -static inline u32 asic3_get_gpio(struct asic3 *asic, unsigned int base, - unsigned int function) +static int asic3_gpio_probe(struct platform_device *pdev, + u16 *gpio_config, int num) { - return asic3_read_register(asic, base + function); -} - -static void asic3_set_gpio(struct asic3 *asic, unsigned int base, - unsigned int function, u32 bits, u32 val) -{ - unsigned long flags; - - spin_lock_irqsave(&asic->lock, flags); - val |= (asic3_read_register(asic, base + function) & ~bits); - - asic3_write_register(asic, base + function, val); - spin_unlock_irqrestore(&asic->lock, flags); -} - -#define asic3_set_gpio_a(asic, fn, bits, val) \ - asic3_set_gpio(asic, ASIC3_GPIO_A_Base, ASIC3_GPIO_##fn, bits, val) -#define asic3_set_gpio_b(asic, fn, bits, val) \ - asic3_set_gpio(asic, ASIC3_GPIO_B_Base, ASIC3_GPIO_##fn, bits, val) -#define asic3_set_gpio_c(asic, fn, bits, val) \ - asic3_set_gpio(asic, ASIC3_GPIO_C_Base, ASIC3_GPIO_##fn, bits, val) -#define asic3_set_gpio_d(asic, fn, bits, val) \ - asic3_set_gpio(asic, ASIC3_GPIO_D_Base, ASIC3_GPIO_##fn, bits, val) - -#define asic3_set_gpio_banks(asic, fn, bits, pdata, field) \ - do { \ - asic3_set_gpio_a((asic), fn, (bits), (pdata)->gpio_a.field); \ - asic3_set_gpio_b((asic), fn, (bits), (pdata)->gpio_b.field); \ - asic3_set_gpio_c((asic), fn, (bits), (pdata)->gpio_c.field); \ - asic3_set_gpio_d((asic), fn, (bits), (pdata)->gpio_d.field); \ - } while (0) - - -static int asic3_gpio_probe(struct platform_device *pdev) -{ - struct asic3_platform_data *pdata = pdev->dev.platform_data; struct asic3 *asic = platform_get_drvdata(pdev); + u16 alt_reg[ASIC3_NUM_GPIO_BANKS]; + u16 out_reg[ASIC3_NUM_GPIO_BANKS]; + u16 dir_reg[ASIC3_NUM_GPIO_BANKS]; + int i; + memset(alt_reg, 0, ASIC3_NUM_GPIO_BANKS); + memset(out_reg, 0, ASIC3_NUM_GPIO_BANKS); + memset(dir_reg, 0, ASIC3_NUM_GPIO_BANKS); + + /* Enable all GPIOs */ asic3_write_register(asic, ASIC3_GPIO_OFFSET(A, Mask), 0xffff); asic3_write_register(asic, ASIC3_GPIO_OFFSET(B, Mask), 0xffff); asic3_write_register(asic, ASIC3_GPIO_OFFSET(C, Mask), 0xffff); asic3_write_register(asic, ASIC3_GPIO_OFFSET(D, Mask), 0xffff); - asic3_set_gpio_a(asic, SleepMask, 0xffff, 0xffff); - asic3_set_gpio_b(asic, SleepMask, 0xffff, 0xffff); - asic3_set_gpio_c(asic, SleepMask, 0xffff, 0xffff); - asic3_set_gpio_d(asic, SleepMask, 0xffff, 0xffff); - - if (pdata) { - asic3_set_gpio_banks(asic, Out, 0xffff, pdata, init); - asic3_set_gpio_banks(asic, Direction, 0xffff, pdata, dir); - asic3_set_gpio_banks(asic, SleepMask, 0xffff, pdata, - sleep_mask); - asic3_set_gpio_banks(asic, SleepOut, 0xffff, pdata, sleep_out); - asic3_set_gpio_banks(asic, BattFaultOut, 0xffff, pdata, - batt_fault_out); - asic3_set_gpio_banks(asic, SleepConf, 0xffff, pdata, - sleep_conf); - asic3_set_gpio_banks(asic, AltFunction, 0xffff, pdata, - alt_function); + for (i = 0; i < num; i++) { + u8 alt, pin, dir, init, bank_num, bit_num; + u16 config = gpio_config[i]; + + pin = ASIC3_CONFIG_GPIO_PIN(config); + alt = ASIC3_CONFIG_GPIO_ALT(config); + dir = ASIC3_CONFIG_GPIO_DIR(config); + init = ASIC3_CONFIG_GPIO_INIT(config); + + bank_num = ASIC3_GPIO_TO_BANK(pin); + bit_num = ASIC3_GPIO_TO_BIT(pin); + + alt_reg[bank_num] |= (alt << bit_num); + out_reg[bank_num] |= (init << bit_num); + dir_reg[bank_num] |= (dir << bit_num); + } + + for (i = 0; i < ASIC3_NUM_GPIO_BANKS; i++) { + asic3_write_register(asic, + ASIC3_BANK_TO_BASE(i) + + ASIC3_GPIO_Direction, + dir_reg[i]); + asic3_write_register(asic, + ASIC3_BANK_TO_BASE(i) + ASIC3_GPIO_Out, + out_reg[i]); + asic3_write_register(asic, + ASIC3_BANK_TO_BASE(i) + + ASIC3_GPIO_AltFunction, + alt_reg[i]); } return gpiochip_add(&asic->gpio); @@ -598,7 +583,9 @@ static int asic3_probe(struct platform_device *pdev) asic->gpio.direction_input = asic3_gpio_direction_input; asic->gpio.direction_output = asic3_gpio_direction_output; - ret = asic3_gpio_probe(pdev); + ret = asic3_gpio_probe(pdev, + pdata->gpio_config, + pdata->gpio_config_num); if (ret < 0) { printk(KERN_ERR "GPIO probe failed\n"); goto out_irq; diff --git a/include/linux/mfd/asic3.h b/include/linux/mfd/asic3.h index b1c365800ab5..7e47cfb0c440 100644 --- a/include/linux/mfd/asic3.h +++ b/include/linux/mfd/asic3.h @@ -8,7 +8,7 @@ * published by the Free Software Foundation. * * Copyright 2001 Compaq Computer Corporation. - * Copyright 2007 OpendHand. + * Copyright 2007-2008 OpenedHand Ltd. */ #ifndef __ASIC3_H__ @@ -17,15 +17,8 @@ #include struct asic3_platform_data { - struct { - u32 dir; - u32 init; - u32 sleep_mask; - u32 sleep_out; - u32 batt_fault_out; - u32 sleep_conf; - u32 alt_function; - } gpio_a, gpio_b, gpio_c, gpio_d; + u16 *gpio_config; + unsigned int gpio_config_num; unsigned int bus_shift; @@ -86,6 +79,27 @@ struct asic3_platform_data { */ #define ASIC3_GPIO_Status 0x30 /* R Pin status */ +/* + * ASIC3 GPIO config + * + * Bits 0..6 gpio number + * Bits 7..13 Alternate function + * Bit 14 Direction + * Bit 15 Initial value + * + */ +#define ASIC3_CONFIG_GPIO_PIN(config) ((config) & 0x7f) +#define ASIC3_CONFIG_GPIO_ALT(config) (((config) & (0x7f << 7)) >> 7) +#define ASIC3_CONFIG_GPIO_DIR(config) ((config & (1 << 14)) >> 14) +#define ASIC3_CONFIG_GPIO_INIT(config) ((config & (1 << 15)) >> 15) +#define ASIC3_CONFIG_GPIO(gpio, alt, dir, init) (((gpio) & 0x7f) \ + | (((alt) & 0x7f) << 7) | (((dir) & 0x1) << 14) \ + | (((init) & 0x1) << 15)) +#define ASIC3_CONFIG_GPIO_DEFAULT(gpio, dir, init) \ + ASIC3_CONFIG_GPIO((gpio), 0, (dir), (init)) +#define ASIC3_CONFIG_GPIO_DEFAULT_OUT(gpio, init) \ + ASIC3_CONFIG_GPIO((gpio), 0, 1, (init)) + #define ASIC3_SPI_Base 0x0400 #define ASIC3_SPI_Control 0x0000 #define ASIC3_SPI_TxData 0x0004 -- cgit v1.2.3 From 3b8139f8b1457af7b5295d97050b3f9a2545a17a Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Fri, 20 Jun 2008 11:12:21 +0200 Subject: mfd: Use uppercase only for asic3 macros and defines Let's be consistent and use uppercase only, for both macro and defines. Signed-off-by: Samuel Ortiz Signed-off-by: Andrew Morton --- drivers/mfd/asic3.c | 88 +++++++++++++++++++++++------------------------ include/linux/mfd/asic3.h | 56 +++++++++++++++--------------- 2 files changed, 72 insertions(+), 72 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index 1924eb01cc8c..9931581b08a1 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -55,8 +55,8 @@ static inline u32 asic3_read_register(struct asic3 *asic, /* IRQs */ #define MAX_ASIC_ISR_LOOPS 20 -#define ASIC3_GPIO_Base_INCR \ - (ASIC3_GPIO_B_Base - ASIC3_GPIO_A_Base) +#define ASIC3_GPIO_BASE_INCR \ + (ASIC3_GPIO_B_BASE - ASIC3_GPIO_A_BASE) static void asic3_irq_flip_edge(struct asic3 *asic, u32 base, int bit) @@ -66,10 +66,10 @@ static void asic3_irq_flip_edge(struct asic3 *asic, spin_lock_irqsave(&asic->lock, flags); edge = asic3_read_register(asic, - base + ASIC3_GPIO_EdgeTrigger); + base + ASIC3_GPIO_EDGE_TRIGGER); edge ^= bit; asic3_write_register(asic, - base + ASIC3_GPIO_EdgeTrigger, edge); + base + ASIC3_GPIO_EDGE_TRIGGER, edge); spin_unlock_irqrestore(&asic->lock, flags); } @@ -89,7 +89,7 @@ static void asic3_irq_demux(unsigned int irq, struct irq_desc *desc) spin_lock_irqsave(&asic->lock, flags); status = asic3_read_register(asic, - ASIC3_OFFSET(INTR, PIntStat)); + ASIC3_OFFSET(INTR, P_INT_STAT)); spin_unlock_irqrestore(&asic->lock, flags); /* Check all ten register bits */ @@ -101,17 +101,17 @@ static void asic3_irq_demux(unsigned int irq, struct irq_desc *desc) if (status & (1 << bank)) { unsigned long base, istat; - base = ASIC3_GPIO_A_Base - + bank * ASIC3_GPIO_Base_INCR; + base = ASIC3_GPIO_A_BASE + + bank * ASIC3_GPIO_BASE_INCR; spin_lock_irqsave(&asic->lock, flags); istat = asic3_read_register(asic, base + - ASIC3_GPIO_IntStatus); + ASIC3_GPIO_INT_STATUS); /* Clearing IntStatus */ asic3_write_register(asic, base + - ASIC3_GPIO_IntStatus, 0); + ASIC3_GPIO_INT_STATUS, 0); spin_unlock_irqrestore(&asic->lock, flags); for (i = 0; i < ASIC3_GPIOS_PER_BANK; i++) { @@ -154,7 +154,7 @@ static inline int asic3_irq_to_bank(struct asic3 *asic, int irq) n = (irq - asic->irq_base) >> 4; - return (n * (ASIC3_GPIO_B_Base - ASIC3_GPIO_A_Base)); + return (n * (ASIC3_GPIO_B_BASE - ASIC3_GPIO_A_BASE)); } static inline int asic3_irq_to_index(struct asic3 *asic, int irq) @@ -172,9 +172,9 @@ static void asic3_mask_gpio_irq(unsigned int irq) index = asic3_irq_to_index(asic, irq); spin_lock_irqsave(&asic->lock, flags); - val = asic3_read_register(asic, bank + ASIC3_GPIO_Mask); + val = asic3_read_register(asic, bank + ASIC3_GPIO_MASK); val |= 1 << index; - asic3_write_register(asic, bank + ASIC3_GPIO_Mask, val); + asic3_write_register(asic, bank + ASIC3_GPIO_MASK, val); spin_unlock_irqrestore(&asic->lock, flags); } @@ -186,15 +186,15 @@ static void asic3_mask_irq(unsigned int irq) spin_lock_irqsave(&asic->lock, flags); regval = asic3_read_register(asic, - ASIC3_INTR_Base + - ASIC3_INTR_IntMask); + ASIC3_INTR_BASE + + ASIC3_INTR_INT_MASK); regval &= ~(ASIC3_INTMASK_MASK0 << (irq - (asic->irq_base + ASIC3_NUM_GPIOS))); asic3_write_register(asic, - ASIC3_INTR_Base + - ASIC3_INTR_IntMask, + ASIC3_INTR_BASE + + ASIC3_INTR_INT_MASK, regval); spin_unlock_irqrestore(&asic->lock, flags); } @@ -209,9 +209,9 @@ static void asic3_unmask_gpio_irq(unsigned int irq) index = asic3_irq_to_index(asic, irq); spin_lock_irqsave(&asic->lock, flags); - val = asic3_read_register(asic, bank + ASIC3_GPIO_Mask); + val = asic3_read_register(asic, bank + ASIC3_GPIO_MASK); val &= ~(1 << index); - asic3_write_register(asic, bank + ASIC3_GPIO_Mask, val); + asic3_write_register(asic, bank + ASIC3_GPIO_MASK, val); spin_unlock_irqrestore(&asic->lock, flags); } @@ -223,15 +223,15 @@ static void asic3_unmask_irq(unsigned int irq) spin_lock_irqsave(&asic->lock, flags); regval = asic3_read_register(asic, - ASIC3_INTR_Base + - ASIC3_INTR_IntMask); + ASIC3_INTR_BASE + + ASIC3_INTR_INT_MASK); regval |= (ASIC3_INTMASK_MASK0 << (irq - (asic->irq_base + ASIC3_NUM_GPIOS))); asic3_write_register(asic, - ASIC3_INTR_Base + - ASIC3_INTR_IntMask, + ASIC3_INTR_BASE + + ASIC3_INTR_INT_MASK, regval); spin_unlock_irqrestore(&asic->lock, flags); } @@ -249,11 +249,11 @@ static int asic3_gpio_irq_type(unsigned int irq, unsigned int type) spin_lock_irqsave(&asic->lock, flags); level = asic3_read_register(asic, - bank + ASIC3_GPIO_LevelTrigger); + bank + ASIC3_GPIO_LEVEL_TRIGGER); edge = asic3_read_register(asic, - bank + ASIC3_GPIO_EdgeTrigger); + bank + ASIC3_GPIO_EDGE_TRIGGER); trigger = asic3_read_register(asic, - bank + ASIC3_GPIO_TriggerType); + bank + ASIC3_GPIO_TRIGGER_TYPE); asic->irq_bothedge[(irq - asic->irq_base) >> 4] &= ~bit; if (type == IRQT_RISING) { @@ -283,11 +283,11 @@ static int asic3_gpio_irq_type(unsigned int irq, unsigned int type) */ dev_notice(asic->dev, "irq type not changed\n"); } - asic3_write_register(asic, bank + ASIC3_GPIO_LevelTrigger, + asic3_write_register(asic, bank + ASIC3_GPIO_LEVEL_TRIGGER, level); - asic3_write_register(asic, bank + ASIC3_GPIO_EdgeTrigger, + asic3_write_register(asic, bank + ASIC3_GPIO_EDGE_TRIGGER, edge); - asic3_write_register(asic, bank + ASIC3_GPIO_TriggerType, + asic3_write_register(asic, bank + ASIC3_GPIO_TRIGGER_TYPE, trigger); spin_unlock_irqrestore(&asic->lock, flags); return 0; @@ -336,7 +336,7 @@ static int asic3_irq_probe(struct platform_device *pdev) set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); } - asic3_write_register(asic, ASIC3_OFFSET(INTR, IntMask), + asic3_write_register(asic, ASIC3_OFFSET(INTR, INT_MASK), ASIC3_INTMASK_GINTMASK); set_irq_chained_handler(asic->irq_nr, asic3_irq_demux); @@ -374,7 +374,7 @@ static int asic3_gpio_direction(struct gpio_chip *chip, asic = container_of(chip, struct asic3, gpio); gpio_base = ASIC3_GPIO_TO_BASE(offset); - if (gpio_base > ASIC3_GPIO_D_Base) { + if (gpio_base > ASIC3_GPIO_D_BASE) { dev_err(asic->dev, "Invalid base (0x%x) for gpio %d\n", gpio_base, offset); return -EINVAL; @@ -382,7 +382,7 @@ static int asic3_gpio_direction(struct gpio_chip *chip, spin_lock_irqsave(&asic->lock, flags); - out_reg = asic3_read_register(asic, gpio_base + ASIC3_GPIO_Direction); + out_reg = asic3_read_register(asic, gpio_base + ASIC3_GPIO_DIRECTION); /* Input is 0, Output is 1 */ if (out) @@ -390,7 +390,7 @@ static int asic3_gpio_direction(struct gpio_chip *chip, else out_reg &= ~mask; - asic3_write_register(asic, gpio_base + ASIC3_GPIO_Direction, out_reg); + asic3_write_register(asic, gpio_base + ASIC3_GPIO_DIRECTION, out_reg); spin_unlock_irqrestore(&asic->lock, flags); @@ -420,13 +420,13 @@ static int asic3_gpio_get(struct gpio_chip *chip, asic = container_of(chip, struct asic3, gpio); gpio_base = ASIC3_GPIO_TO_BASE(offset); - if (gpio_base > ASIC3_GPIO_D_Base) { + if (gpio_base > ASIC3_GPIO_D_BASE) { dev_err(asic->dev, "Invalid base (0x%x) for gpio %d\n", gpio_base, offset); return -EINVAL; } - return asic3_read_register(asic, gpio_base + ASIC3_GPIO_Status) & mask; + return asic3_read_register(asic, gpio_base + ASIC3_GPIO_STATUS) & mask; } static void asic3_gpio_set(struct gpio_chip *chip, @@ -440,7 +440,7 @@ static void asic3_gpio_set(struct gpio_chip *chip, asic = container_of(chip, struct asic3, gpio); gpio_base = ASIC3_GPIO_TO_BASE(offset); - if (gpio_base > ASIC3_GPIO_D_Base) { + if (gpio_base > ASIC3_GPIO_D_BASE) { dev_err(asic->dev, "Invalid base (0x%x) for gpio %d\n", gpio_base, offset); return; @@ -450,14 +450,14 @@ static void asic3_gpio_set(struct gpio_chip *chip, spin_lock_irqsave(&asic->lock, flags); - out_reg = asic3_read_register(asic, gpio_base + ASIC3_GPIO_Out); + out_reg = asic3_read_register(asic, gpio_base + ASIC3_GPIO_OUT); if (value) out_reg |= mask; else out_reg &= ~mask; - asic3_write_register(asic, gpio_base + ASIC3_GPIO_Out, out_reg); + asic3_write_register(asic, gpio_base + ASIC3_GPIO_OUT, out_reg); spin_unlock_irqrestore(&asic->lock, flags); @@ -478,10 +478,10 @@ static int asic3_gpio_probe(struct platform_device *pdev, memset(dir_reg, 0, ASIC3_NUM_GPIO_BANKS); /* Enable all GPIOs */ - asic3_write_register(asic, ASIC3_GPIO_OFFSET(A, Mask), 0xffff); - asic3_write_register(asic, ASIC3_GPIO_OFFSET(B, Mask), 0xffff); - asic3_write_register(asic, ASIC3_GPIO_OFFSET(C, Mask), 0xffff); - asic3_write_register(asic, ASIC3_GPIO_OFFSET(D, Mask), 0xffff); + asic3_write_register(asic, ASIC3_GPIO_OFFSET(A, MASK), 0xffff); + asic3_write_register(asic, ASIC3_GPIO_OFFSET(B, MASK), 0xffff); + asic3_write_register(asic, ASIC3_GPIO_OFFSET(C, MASK), 0xffff); + asic3_write_register(asic, ASIC3_GPIO_OFFSET(D, MASK), 0xffff); for (i = 0; i < num; i++) { u8 alt, pin, dir, init, bank_num, bit_num; @@ -503,14 +503,14 @@ static int asic3_gpio_probe(struct platform_device *pdev, for (i = 0; i < ASIC3_NUM_GPIO_BANKS; i++) { asic3_write_register(asic, ASIC3_BANK_TO_BASE(i) + - ASIC3_GPIO_Direction, + ASIC3_GPIO_DIRECTION, dir_reg[i]); asic3_write_register(asic, - ASIC3_BANK_TO_BASE(i) + ASIC3_GPIO_Out, + ASIC3_BANK_TO_BASE(i) + ASIC3_GPIO_OUT, out_reg[i]); asic3_write_register(asic, ASIC3_BANK_TO_BASE(i) + - ASIC3_GPIO_AltFunction, + ASIC3_GPIO_ALT_FUNCTION, alt_reg[i]); } diff --git a/include/linux/mfd/asic3.h b/include/linux/mfd/asic3.h index 7e47cfb0c440..6461a2ae69a2 100644 --- a/include/linux/mfd/asic3.h +++ b/include/linux/mfd/asic3.h @@ -45,39 +45,39 @@ struct asic3_platform_data { /* All offsets below are specified with this address bus shift */ #define ASIC3_DEFAULT_ADDR_SHIFT 2 -#define ASIC3_OFFSET(base, reg) (ASIC3_##base##_Base + ASIC3_##base##_##reg) +#define ASIC3_OFFSET(base, reg) (ASIC3_##base##_BASE + ASIC3_##base##_##reg) #define ASIC3_GPIO_OFFSET(base, reg) \ - (ASIC3_GPIO_##base##_Base + ASIC3_GPIO_##reg) + (ASIC3_GPIO_##base##_BASE + ASIC3_GPIO_##reg) -#define ASIC3_GPIO_A_Base 0x0000 -#define ASIC3_GPIO_B_Base 0x0100 -#define ASIC3_GPIO_C_Base 0x0200 -#define ASIC3_GPIO_D_Base 0x0300 +#define ASIC3_GPIO_A_BASE 0x0000 +#define ASIC3_GPIO_B_BASE 0x0100 +#define ASIC3_GPIO_C_BASE 0x0200 +#define ASIC3_GPIO_D_BASE 0x0300 #define ASIC3_GPIO_TO_BANK(gpio) ((gpio) >> 4) #define ASIC3_GPIO_TO_BIT(gpio) ((gpio) - \ (ASIC3_GPIOS_PER_BANK * ((gpio) >> 4))) #define ASIC3_GPIO_TO_MASK(gpio) (1 << ASIC3_GPIO_TO_BIT(gpio)) -#define ASIC3_GPIO_TO_BASE(gpio) (ASIC3_GPIO_A_Base + (((gpio) >> 4) * 0x0100)) -#define ASIC3_BANK_TO_BASE(bank) (ASIC3_GPIO_A_Base + ((bank) * 0x100)) - -#define ASIC3_GPIO_Mask 0x00 /* R/W 0:don't mask */ -#define ASIC3_GPIO_Direction 0x04 /* R/W 0:input */ -#define ASIC3_GPIO_Out 0x08 /* R/W 0:output low */ -#define ASIC3_GPIO_TriggerType 0x0c /* R/W 0:level */ -#define ASIC3_GPIO_EdgeTrigger 0x10 /* R/W 0:falling */ -#define ASIC3_GPIO_LevelTrigger 0x14 /* R/W 0:low level detect */ -#define ASIC3_GPIO_SleepMask 0x18 /* R/W 0:don't mask in sleep mode */ -#define ASIC3_GPIO_SleepOut 0x1c /* R/W level 0:low in sleep mode */ -#define ASIC3_GPIO_BattFaultOut 0x20 /* R/W level 0:low in batt_fault */ -#define ASIC3_GPIO_IntStatus 0x24 /* R/W 0:none, 1:detect */ -#define ASIC3_GPIO_AltFunction 0x28 /* R/W 1:LED register control */ -#define ASIC3_GPIO_SleepConf 0x2c /* +#define ASIC3_GPIO_TO_BASE(gpio) (ASIC3_GPIO_A_BASE + (((gpio) >> 4) * 0x0100)) +#define ASIC3_BANK_TO_BASE(bank) (ASIC3_GPIO_A_BASE + ((bank) * 0x100)) + +#define ASIC3_GPIO_MASK 0x00 /* R/W 0:don't mask */ +#define ASIC3_GPIO_DIRECTION 0x04 /* R/W 0:input */ +#define ASIC3_GPIO_OUT 0x08 /* R/W 0:output low */ +#define ASIC3_GPIO_TRIGGER_TYPE 0x0c /* R/W 0:level */ +#define ASIC3_GPIO_EDGE_TRIGGER 0x10 /* R/W 0:falling */ +#define ASIC3_GPIO_LEVEL_TRIGGER 0x14 /* R/W 0:low level detect */ +#define ASIC3_GPIO_SLEEP_MASK 0x18 /* R/W 0:don't mask in sleep mode */ +#define ASIC3_GPIO_SLEEP_OUT 0x1c /* R/W level 0:low in sleep mode */ +#define ASIC3_GPIO_BAT_FAULT_OUT 0x20 /* R/W level 0:low in batt_fault */ +#define ASIC3_GPIO_INT_STATUS 0x24 /* R/W 0:none, 1:detect */ +#define ASIC3_GPIO_ALT_FUNCTION 0x28 /* R/W 1:LED register control */ +#define ASIC3_GPIO_SLEEP_CONF 0x2c /* * R/W bit 1: autosleep * 0: disable gposlpout in normal mode, * enable gposlpout in sleep mode. */ -#define ASIC3_GPIO_Status 0x30 /* R Pin status */ +#define ASIC3_GPIO_STATUS 0x30 /* R Pin status */ /* * ASIC3 GPIO config @@ -137,7 +137,7 @@ struct asic3_platform_data { #define LED_AUTOSTOP (1 << 5) /* LED ON/OFF auto stop 0:disable, 1:enable */ #define LED_ALWAYS (1 << 6) /* LED Interrupt Mask 0:No mask, 1:mask */ -#define ASIC3_CLOCK_Base 0x0A00 +#define ASIC3_CLOCK_BASE 0x0A00 #define ASIC3_CLOCK_CDEX 0x00 #define ASIC3_CLOCK_SEL 0x04 @@ -168,12 +168,12 @@ struct asic3_platform_data { #define CLOCK_SEL_CX (1 << 2) -#define ASIC3_INTR_Base 0x0B00 +#define ASIC3_INTR_BASE 0x0B00 -#define ASIC3_INTR_IntMask 0x00 /* Interrupt mask control */ -#define ASIC3_INTR_PIntStat 0x04 /* Peripheral interrupt status */ -#define ASIC3_INTR_IntCPS 0x08 /* Interrupt timer clock pre-scale */ -#define ASIC3_INTR_IntTBS 0x0c /* Interrupt timer set */ +#define ASIC3_INTR_INT_MASK 0x00 /* Interrupt mask control */ +#define ASIC3_INTR_P_INT_STAT 0x04 /* Peripheral interrupt status */ +#define ASIC3_INTR_INT_CPS 0x08 /* Interrupt timer clock pre-scale */ +#define ASIC3_INTR_INT_TBS 0x0c /* Interrupt timer set */ #define ASIC3_INTMASK_GINTMASK (1 << 0) /* Global INTs mask 1:enable */ #define ASIC3_INTMASK_GINTEL (1 << 1) /* 1: rising edge, 0: hi level */ -- cgit v1.2.3 From 4a67b528e0be5d855b1a7bb71ec769d954765f6c Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 3 Jul 2008 12:27:32 +0200 Subject: mfd: add ASIC3_CONFIG_GPIO templates As ASIC3 GPIO alternate function configuration is expected to be similar for several devices, it is convenient to define descriptive macros. This patch is inspired by the PXA MFP configuration, the alternate functions were observed on hx4700 and blueangel. Signed-off-by: Philipp Zabel Signed-off-by: Samuel Ortiz --- include/linux/mfd/asic3.h | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'include/linux') diff --git a/include/linux/mfd/asic3.h b/include/linux/mfd/asic3.h index 6461a2ae69a2..0eae3083da43 100644 --- a/include/linux/mfd/asic3.h +++ b/include/linux/mfd/asic3.h @@ -100,6 +100,35 @@ struct asic3_platform_data { #define ASIC3_CONFIG_GPIO_DEFAULT_OUT(gpio, init) \ ASIC3_CONFIG_GPIO((gpio), 0, 1, (init)) +/* + * Alternate functions + */ +#define ASIC3_GPIOA11_PWM0 ASIC3_CONFIG_GPIO(11, 1, 1, 0) +#define ASIC3_GPIOA12_PWM1 ASIC3_CONFIG_GPIO(12, 1, 1, 0) +#define ASIC3_GPIOA15_CONTROL_CX ASIC3_CONFIG_GPIO(15, 1, 1, 0) +#define ASIC3_GPIOC0_LED0 ASIC3_CONFIG_GPIO(32, 1, 1, 0) +#define ASIC3_GPIOC1_LED1 ASIC3_CONFIG_GPIO(33, 1, 1, 0) +#define ASIC3_GPIOC2_LED2 ASIC3_CONFIG_GPIO(34, 1, 1, 0) +#define ASIC3_GPIOC3_SPI_RXD ASIC3_CONFIG_GPIO(35, 1, 0, 0) +#define ASIC3_GPIOC4_CF_nCD ASIC3_CONFIG_GPIO(36, 1, 0, 0) +#define ASIC3_GPIOC4_SPI_TXD ASIC3_CONFIG_GPIO(36, 1, 1, 0) +#define ASIC3_GPIOC5_SPI_CLK ASIC3_CONFIG_GPIO(37, 1, 1, 0) +#define ASIC3_GPIOC5_nCIOW ASIC3_CONFIG_GPIO(37, 1, 1, 0) +#define ASIC3_GPIOC6_nCIOR ASIC3_CONFIG_GPIO(38, 1, 1, 0) +#define ASIC3_GPIOC7_nPCE_1 ASIC3_CONFIG_GPIO(39, 1, 0, 0) +#define ASIC3_GPIOC8_nPCE_2 ASIC3_CONFIG_GPIO(40, 1, 0, 0) +#define ASIC3_GPIOC9_nPOE ASIC3_CONFIG_GPIO(41, 1, 0, 0) +#define ASIC3_GPIOC10_nPWE ASIC3_CONFIG_GPIO(42, 1, 0, 0) +#define ASIC3_GPIOC11_PSKTSEL ASIC3_CONFIG_GPIO(43, 1, 0, 0) +#define ASIC3_GPIOC12_nPREG ASIC3_CONFIG_GPIO(44, 1, 0, 0) +#define ASIC3_GPIOC13_nPWAIT ASIC3_CONFIG_GPIO(45, 1, 1, 0) +#define ASIC3_GPIOC14_nPIOIS16 ASIC3_CONFIG_GPIO(46, 1, 1, 0) +#define ASIC3_GPIOC15_nPIOR ASIC3_CONFIG_GPIO(47, 1, 0, 0) +#define ASIC3_GPIOD11_nCIOIS16 ASIC3_CONFIG_GPIO(59, 1, 0, 0) +#define ASIC3_GPIOD12_nCWAIT ASIC3_CONFIG_GPIO(60, 1, 0, 0) +#define ASIC3_GPIOD15_nPIOW ASIC3_CONFIG_GPIO(63, 1, 0, 0) + + #define ASIC3_SPI_Base 0x0400 #define ASIC3_SPI_Control 0x0000 #define ASIC3_SPI_TxData 0x0004 -- cgit v1.2.3 From 279cac484e55317456900fe3567c7cb5bd46fd5f Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 10 Jul 2008 02:16:27 +0200 Subject: mfd: remove DS1WM register definitions from asic3.h There is a dedicated ds1wm driver, no need to duplicate this information here. Signed-off-by: Philipp Zabel Signed-off-by: Samuel Ortiz --- include/linux/mfd/asic3.h | 38 +++----------------------------------- 1 file changed, 3 insertions(+), 35 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mfd/asic3.h b/include/linux/mfd/asic3.h index 0eae3083da43..8f8c46c41f44 100644 --- a/include/linux/mfd/asic3.h +++ b/include/linux/mfd/asic3.h @@ -265,44 +265,12 @@ struct asic3_platform_data { #define ASIC3_EXTCF_CF_SLEEP (1 << 15) /* CF sleep mode control */ /********************************************* - * The Onewire interface registers - * - * OWM_CMD - * OWM_DAT - * OWM_INTR - * OWM_INTEN - * OWM_CLKDIV + * The Onewire interface (DS1WM) is handled + * by the ds1wm driver. * *********************************************/ -#define ASIC3_OWM_Base 0xC00 - -#define ASIC3_OWM_CMD 0x00 -#define ASIC3_OWM_DAT 0x04 -#define ASIC3_OWM_INTR 0x08 -#define ASIC3_OWM_INTEN 0x0C -#define ASIC3_OWM_CLKDIV 0x10 - -#define ASIC3_OWM_CMD_ONEWR (1 << 0) -#define ASIC3_OWM_CMD_SRA (1 << 1) -#define ASIC3_OWM_CMD_DQO (1 << 2) -#define ASIC3_OWM_CMD_DQI (1 << 3) - -#define ASIC3_OWM_INTR_PD (1 << 0) -#define ASIC3_OWM_INTR_PDR (1 << 1) -#define ASIC3_OWM_INTR_TBE (1 << 2) -#define ASIC3_OWM_INTR_TEMP (1 << 3) -#define ASIC3_OWM_INTR_RBF (1 << 4) - -#define ASIC3_OWM_INTEN_EPD (1 << 0) -#define ASIC3_OWM_INTEN_IAS (1 << 1) -#define ASIC3_OWM_INTEN_ETBE (1 << 2) -#define ASIC3_OWM_INTEN_ETMT (1 << 3) -#define ASIC3_OWM_INTEN_ERBF (1 << 4) - -#define ASIC3_OWM_CLKDIV_PRE (3 << 0) /* two bits wide at bit 0 */ -#define ASIC3_OWM_CLKDIV_DIV (7 << 2) /* 3 bits wide at bit 2 */ - +#define ASIC3_OWM_BASE 0xC00 /***************************************************************************** * The SD configuration registers are at a completely different location -- cgit v1.2.3 From 99cdb0c8c5e0e43652d25951a85bac82a1231591 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 10 Jul 2008 02:17:02 +0200 Subject: mfd: let asic3 use mem resource instead of bus_shift The bus_shift parameter in platform_data is not needed as we can tell the driver with the IOMEM_RESOURCE whether the ASIC is located on a 16bit or 32bit memory bus. The htc-egpio driver uses a more descriptive bus_width parameter, but for drivers where the register map size fixed, we don't even need this. Signed-off-by: Philipp Zabel Signed-off-by: Samuel Ortiz --- drivers/mfd/asic3.c | 11 +++++------ include/linux/mfd/asic3.h | 5 ++--- 2 files changed, 7 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index f2fb233b0ff3..3b870e7fb3e1 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -313,6 +313,7 @@ static int __init asic3_irq_probe(struct platform_device *pdev) struct asic3 *asic = platform_get_drvdata(pdev); unsigned long clksel = 0; unsigned int irq, irq_base; + int map_size; asic->irq_nr = platform_get_irq(pdev, 0); if (asic->irq_nr < 0) @@ -551,8 +552,8 @@ static int __init asic3_probe(struct platform_device *pdev) goto out_free; } - - asic->mapping = ioremap(mem->start, PAGE_SIZE); + map_size = mem->end - mem->start + 1; + asic->mapping = ioremap(mem->start, map_size); if (!asic->mapping) { ret = -ENOMEM; dev_err(asic->dev, "Couldn't ioremap\n"); @@ -561,10 +562,8 @@ static int __init asic3_probe(struct platform_device *pdev) asic->irq_base = pdata->irq_base; - if (pdata && pdata->bus_shift) - asic->bus_shift = 2 - pdata->bus_shift; - else - asic->bus_shift = 0; + /* calculate bus shift from mem resource */ + asic->bus_shift = 2 - (map_size >> 12); clksel = 0; asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL), clksel); diff --git a/include/linux/mfd/asic3.h b/include/linux/mfd/asic3.h index 8f8c46c41f44..322cd6deb9f0 100644 --- a/include/linux/mfd/asic3.h +++ b/include/linux/mfd/asic3.h @@ -20,8 +20,6 @@ struct asic3_platform_data { u16 *gpio_config; unsigned int gpio_config_num; - unsigned int bus_shift; - unsigned int irq_base; unsigned int gpio_base; @@ -498,6 +496,7 @@ struct asic3_platform_data { #define ASIC3_SDIO_CTRL_LEDCtrl 0x7C #define ASIC3_SDIO_CTRL_SoftwareReset 0x1C0 -#define ASIC3_MAP_SIZE 0x2000 +#define ASIC3_MAP_SIZE_32BIT 0x2000 +#define ASIC3_MAP_SIZE_16BIT 0x1000 #endif /* __ASIC3_H__ */ -- cgit v1.2.3 From 6bb0e3a59a089e23eecc0af3b6f6012b2a9affba Mon Sep 17 00:00:00 2001 From: Haavard Skinnemoen Date: Wed, 16 Jul 2008 21:52:36 +0100 Subject: Subject: [PATCH 1/2] serial: Add flush_buffer() operation to uart_ops Serial drivers using DMA (like the atmel_serial driver) tend to get very confused when the xmit buffer is flushed and nobody told them. They also tend to spew a lot of garbage since the DMA engine keeps running after the buffer is flushed and possibly refilled with unrelated data. This patch adds a new flush_buffer operation to the uart_ops struct, along with a call to it from uart_flush_buffer() right after the xmit buffer has been cleared. The driver can implement this in order to syncronize its internal DMA state with the xmit buffer when the buffer is flushed. Signed-off-by: Haavard Skinnemoen Acked-by: Alan Cox Signed-off-by: Linus Torvalds --- Documentation/serial/driver | 11 +++++++++++ drivers/serial/serial_core.c | 2 ++ include/linux/serial_core.h | 1 + 3 files changed, 14 insertions(+) (limited to 'include/linux') diff --git a/Documentation/serial/driver b/Documentation/serial/driver index 88ad615dd338..77ba0afbe4db 100644 --- a/Documentation/serial/driver +++ b/Documentation/serial/driver @@ -186,6 +186,17 @@ hardware. Locking: port_sem taken. Interrupts: caller dependent. + flush_buffer(port) + Flush any write buffers, reset any DMA state and stop any + ongoing DMA transfers. + + This will be called whenever the port->info->xmit circular + buffer is cleared. + + Locking: port->lock taken. + Interrupts: locally disabled. + This call must not sleep + set_termios(port,termios,oldtermios) Change the port parameters, including word length, parity, stop bits. Update read_status_mask and ignore_status_mask to indicate diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index 42d2e108b679..9884bc9eecb1 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -573,6 +573,8 @@ static void uart_flush_buffer(struct tty_struct *tty) spin_lock_irqsave(&port->lock, flags); uart_circ_clear(&state->info->xmit); + if (port->ops->flush_buffer) + port->ops->flush_buffer(port); spin_unlock_irqrestore(&port->lock, flags); tty_wakeup(tty); } diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index d8f31de632c5..1d2faa6592ae 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -190,6 +190,7 @@ struct uart_ops { void (*break_ctl)(struct uart_port *, int ctl); int (*startup)(struct uart_port *); void (*shutdown)(struct uart_port *); + void (*flush_buffer)(struct uart_port *); void (*set_termios)(struct uart_port *, struct ktermios *new, struct ktermios *old); void (*set_ldisc)(struct uart_port *); -- cgit v1.2.3 From a352def21a642133758b868c71bee12ab34ad5c5 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 16 Jul 2008 21:53:12 +0100 Subject: tty: Ldisc revamp Move the line disciplines towards a conventional ->ops arrangement. For the moment the actual 'tty_ldisc' struct in the tty is kept as part of the tty struct but this can then be changed if it turns out that when it all settles down we want to refcount ldiscs separately to the tty. Pull the ldisc code out of /proc and put it with our ldisc code. Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/bluetooth/hci_ldisc.c | 6 +- drivers/char/cyclades.c | 3 +- drivers/char/epca.c | 4 +- drivers/char/ip2/i2lib.c | 4 +- drivers/char/ip2/ip2main.c | 7 +- drivers/char/n_hdlc.c | 6 +- drivers/char/n_r3964.c | 2 +- drivers/char/n_tty.c | 2 +- drivers/char/pcmcia/synclink_cs.c | 4 +- drivers/char/pty.c | 10 +- drivers/char/selection.c | 3 +- drivers/char/synclink.c | 4 +- drivers/char/synclink_gt.c | 4 +- drivers/char/synclinkmp.c | 4 +- drivers/char/tty_io.c | 336 +++++++++++++++++++++++-------------- drivers/char/tty_ioctl.c | 16 +- drivers/input/serio/serport.c | 2 +- drivers/isdn/capi/capi.c | 4 +- drivers/isdn/gigaset/ser-gigaset.c | 2 +- drivers/net/hamradio/6pack.c | 2 +- drivers/net/hamradio/mkiss.c | 2 +- drivers/net/irda/irtty-sir.c | 2 +- drivers/net/ppp_async.c | 2 +- drivers/net/ppp_synctty.c | 2 +- drivers/net/slip.c | 2 +- drivers/net/wan/pc300_tty.c | 4 +- drivers/net/wan/x25_asy.c | 2 +- fs/proc/proc_tty.c | 48 ------ include/linux/tty.h | 9 +- include/linux/tty_ldisc.h | 7 +- net/bluetooth/rfcomm/tty.c | 13 +- net/irda/ircomm/ircomm_tty.c | 14 +- 32 files changed, 286 insertions(+), 246 deletions(-) (limited to 'include/linux') diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index e5cd856a2fea..69df187d74ce 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -282,8 +282,8 @@ static int hci_uart_tty_open(struct tty_struct *tty) /* FIXME: why is this needed. Note don't use ldisc_ref here as the open path is before the ldisc is referencable */ - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + if (tty->ldisc.ops->flush_buffer) + tty->ldisc.ops->flush_buffer(tty); tty_driver_flush_buffer(tty); return 0; @@ -514,7 +514,7 @@ static unsigned int hci_uart_tty_poll(struct tty_struct *tty, static int __init hci_uart_init(void) { - static struct tty_ldisc hci_uart_ldisc; + static struct tty_ldisc_ops hci_uart_ldisc; int err; BT_INFO("HCI UART driver ver %s", VERSION); diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 6bff9d87dc57..a957dbcc5a46 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -5246,7 +5246,8 @@ cyclades_get_proc_info(char *buf, char **start, off_t offset, int length, HZ, info->idle_stats.recv_bytes, (cur_jifs - info->idle_stats.recv_idle)/ HZ, info->idle_stats.overruns, - (long)info->tty->ldisc.num); + /* FIXME: double check locking */ + (long)info->tty->ldisc.ops->num); else size = sprintf(buf + len, "%3d %8lu %10lu %8lu " "%10lu %8lu %9lu %6ld\n", diff --git a/drivers/char/epca.c b/drivers/char/epca.c index 60a4df7dac12..aa8e19f44b45 100644 --- a/drivers/char/epca.c +++ b/drivers/char/epca.c @@ -2262,8 +2262,8 @@ static int pc_ioctl(struct tty_struct *tty, struct file *file, tty_wait_until_sent(tty, 0); } else { /* ldisc lock already held in ioctl */ - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + if (tty->ldisc.ops->flush_buffer) + tty->ldisc.ops->flush_buffer(tty); } unlock_kernel(); /* Fall Thru */ diff --git a/drivers/char/ip2/i2lib.c b/drivers/char/ip2/i2lib.c index 938879cc7bcc..0061e18aff60 100644 --- a/drivers/char/ip2/i2lib.c +++ b/drivers/char/ip2/i2lib.c @@ -868,11 +868,11 @@ i2Input(i2ChanStrPtr pCh) amountToMove = count; } // Move the first block - pCh->pTTY->ldisc.receive_buf( pCh->pTTY, + pCh->pTTY->ldisc.ops->receive_buf( pCh->pTTY, &(pCh->Ibuf[stripIndex]), NULL, amountToMove ); // If we needed to wrap, do the second data move if (count > amountToMove) { - pCh->pTTY->ldisc.receive_buf( pCh->pTTY, + pCh->pTTY->ldisc.ops->receive_buf( pCh->pTTY, pCh->Ibuf, NULL, count - amountToMove ); } // Bump and wrap the stripIndex all at once by the amount of data read. This diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c index 9a2394cda943..5dc74404058f 100644 --- a/drivers/char/ip2/ip2main.c +++ b/drivers/char/ip2/ip2main.c @@ -1289,11 +1289,12 @@ static void do_input(struct work_struct *work) // code duplicated from n_tty (ldisc) static inline void isig(int sig, struct tty_struct *tty, int flush) { + /* FIXME: This is completely bogus */ if (tty->pgrp) kill_pgrp(tty->pgrp, sig, 1); if (flush || !L_NOFLSH(tty)) { - if ( tty->ldisc.flush_buffer ) - tty->ldisc.flush_buffer(tty); + if ( tty->ldisc.ops->flush_buffer ) + tty->ldisc.ops->flush_buffer(tty); i2InputFlush( tty->driver_data ); } } @@ -1342,7 +1343,7 @@ static void do_status(struct work_struct *work) } tmp = pCh->pTTY->real_raw; pCh->pTTY->real_raw = 0; - pCh->pTTY->ldisc.receive_buf( pCh->pTTY, &brkc, &brkf, 1 ); + pCh->pTTY->ldisc->ops.receive_buf( pCh->pTTY, &brkc, &brkf, 1 ); pCh->pTTY->real_raw = tmp; } #endif /* NEVER_HAPPENS_AS_SETUP_XXX */ diff --git a/drivers/char/n_hdlc.c b/drivers/char/n_hdlc.c index a35bfd7ee80e..ed4e03333ab4 100644 --- a/drivers/char/n_hdlc.c +++ b/drivers/char/n_hdlc.c @@ -199,7 +199,7 @@ static void n_hdlc_tty_wakeup(struct tty_struct *tty); #define tty2n_hdlc(tty) ((struct n_hdlc *) ((tty)->disc_data)) #define n_hdlc2tty(n_hdlc) ((n_hdlc)->tty) -static struct tty_ldisc n_hdlc_ldisc = { +static struct tty_ldisc_ops n_hdlc_ldisc = { .owner = THIS_MODULE, .magic = TTY_LDISC_MAGIC, .name = "hdlc", @@ -342,8 +342,8 @@ static int n_hdlc_tty_open (struct tty_struct *tty) #endif /* Flush any pending characters in the driver and discipline. */ - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); + if (tty->ldisc.ops->flush_buffer) + tty->ldisc.ops->flush_buffer(tty); tty_driver_flush_buffer(tty); diff --git a/drivers/char/n_r3964.c b/drivers/char/n_r3964.c index 902169062332..ae377aa473ba 100644 --- a/drivers/char/n_r3964.c +++ b/drivers/char/n_r3964.c @@ -143,7 +143,7 @@ static unsigned int r3964_poll(struct tty_struct *tty, struct file *file, static void r3964_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count); -static struct tty_ldisc tty_ldisc_N_R3964 = { +static struct tty_ldisc_ops tty_ldisc_N_R3964 = { .owner = THIS_MODULE, .magic = TTY_LDISC_MAGIC, .name = "R3964", diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index 8096389b0dc2..708c2b1dbe51 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -1573,7 +1573,7 @@ static unsigned int normal_poll(struct tty_struct *tty, struct file *file, return mask; } -struct tty_ldisc tty_ldisc_N_TTY = { +struct tty_ldisc_ops tty_ldisc_N_TTY = { .magic = TTY_LDISC_MAGIC, .name = "n_tty", .open = n_tty_open, diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index 1dd0e992c83d..95743e2682fe 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -514,8 +514,8 @@ static void ldisc_receive_buf(struct tty_struct *tty, return; ld = tty_ldisc_ref(tty); if (ld) { - if (ld->receive_buf) - ld->receive_buf(tty, data, flags, count); + if (ld->ops->receive_buf) + ld->ops->receive_buf(tty, data, flags, count); tty_ldisc_deref(ld); } } diff --git a/drivers/char/pty.c b/drivers/char/pty.c index 0a05c038ae6f..76b27932d229 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -111,7 +111,7 @@ static int pty_write(struct tty_struct * tty, const unsigned char *buf, int coun c = to->receive_room; if (c > count) c = count; - to->ldisc.receive_buf(to, buf, NULL, c); + to->ldisc.ops->receive_buf(to, buf, NULL, c); return c; } @@ -149,11 +149,11 @@ static int pty_chars_in_buffer(struct tty_struct *tty) int count; /* We should get the line discipline lock for "tty->link" */ - if (!to || !to->ldisc.chars_in_buffer) + if (!to || !to->ldisc.ops->chars_in_buffer) return 0; /* The ldisc must report 0 if no characters available to be read */ - count = to->ldisc.chars_in_buffer(to); + count = to->ldisc.ops->chars_in_buffer(to); if (tty->driver->subtype == PTY_TYPE_SLAVE) return count; @@ -186,8 +186,8 @@ static void pty_flush_buffer(struct tty_struct *tty) if (!to) return; - if (to->ldisc.flush_buffer) - to->ldisc.flush_buffer(to); + if (to->ldisc.ops->flush_buffer) + to->ldisc.ops->flush_buffer(to); if (to->packet) { spin_lock_irqsave(&tty->ctrl_lock, flags); diff --git a/drivers/char/selection.c b/drivers/char/selection.c index d63f5ccc29e6..2978a49a172b 100644 --- a/drivers/char/selection.c +++ b/drivers/char/selection.c @@ -327,7 +327,8 @@ int paste_selection(struct tty_struct *tty) } count = sel_buffer_lth - pasted; count = min(count, tty->receive_room); - tty->ldisc.receive_buf(tty, sel_buffer + pasted, NULL, count); + tty->ldisc.ops->receive_buf(tty, sel_buffer + pasted, + NULL, count); pasted += count; } remove_wait_queue(&vc->paste_wait, &wait); diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index ac5080df2565..5e4b2e638d0c 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -975,8 +975,8 @@ static void ldisc_receive_buf(struct tty_struct *tty, return; ld = tty_ldisc_ref(tty); if (ld) { - if (ld->receive_buf) - ld->receive_buf(tty, data, flags, count); + if (ld->ops->receive_buf) + ld->ops->receive_buf(tty, data, flags, count); tty_ldisc_deref(ld); } } diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index 55c1653be00c..e473778cd6fa 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -641,8 +641,8 @@ static void ldisc_receive_buf(struct tty_struct *tty, return; ld = tty_ldisc_ref(tty); if (ld) { - if (ld->receive_buf) - ld->receive_buf(tty, data, flags, count); + if (ld->ops->receive_buf) + ld->ops->receive_buf(tty, data, flags, count); tty_ldisc_deref(ld); } } diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c index bec54866e0bb..5341b5aaf8bc 100644 --- a/drivers/char/synclinkmp.c +++ b/drivers/char/synclinkmp.c @@ -712,8 +712,8 @@ static void ldisc_receive_buf(struct tty_struct *tty, return; ld = tty_ldisc_ref(tty); if (ld) { - if (ld->receive_buf) - ld->receive_buf(tty, data, flags, count); + if (ld->ops->receive_buf) + ld->ops->receive_buf(tty, data, flags, count); tty_ldisc_deref(ld); } } diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 047a17339f83..54c4ada460ee 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -95,8 +95,9 @@ #include #include #include +#include -#include +#include #include #include @@ -682,7 +683,7 @@ static void tty_set_termios_ldisc(struct tty_struct *tty, int num) static DEFINE_SPINLOCK(tty_ldisc_lock); static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait); /* Line disc dispatch table */ -static struct tty_ldisc tty_ldiscs[NR_LDISCS]; +static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; /** * tty_register_ldisc - install a line discipline @@ -697,7 +698,7 @@ static struct tty_ldisc tty_ldiscs[NR_LDISCS]; * takes tty_ldisc_lock to guard against ldisc races */ -int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc) +int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc) { unsigned long flags; int ret = 0; @@ -706,10 +707,9 @@ int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc) return -EINVAL; spin_lock_irqsave(&tty_ldisc_lock, flags); - tty_ldiscs[disc] = *new_ldisc; - tty_ldiscs[disc].num = disc; - tty_ldiscs[disc].flags |= LDISC_FLAG_DEFINED; - tty_ldiscs[disc].refcount = 0; + tty_ldiscs[disc] = new_ldisc; + new_ldisc->num = disc; + new_ldisc->refcount = 0; spin_unlock_irqrestore(&tty_ldisc_lock, flags); return ret; @@ -737,19 +737,56 @@ int tty_unregister_ldisc(int disc) return -EINVAL; spin_lock_irqsave(&tty_ldisc_lock, flags); - if (tty_ldiscs[disc].refcount) + if (tty_ldiscs[disc]->refcount) ret = -EBUSY; else - tty_ldiscs[disc].flags &= ~LDISC_FLAG_DEFINED; + tty_ldiscs[disc] = NULL; spin_unlock_irqrestore(&tty_ldisc_lock, flags); return ret; } EXPORT_SYMBOL(tty_unregister_ldisc); + +/** + * tty_ldisc_try_get - try and reference an ldisc + * @disc: ldisc number + * @ld: tty ldisc structure to complete + * + * Attempt to open and lock a line discipline into place. Return + * the line discipline refcounted and assigned in ld. On an error + * report the error code back + */ + +static int tty_ldisc_try_get(int disc, struct tty_ldisc *ld) +{ + unsigned long flags; + struct tty_ldisc_ops *ldops; + int err = -EINVAL; + + spin_lock_irqsave(&tty_ldisc_lock, flags); + ld->ops = NULL; + ldops = tty_ldiscs[disc]; + /* Check the entry is defined */ + if (ldops) { + /* If the module is being unloaded we can't use it */ + if (!try_module_get(ldops->owner)) + err = -EAGAIN; + else { + /* lock it */ + ldops->refcount++; + ld->ops = ldops; + err = 0; + } + } + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + return err; +} + /** * tty_ldisc_get - take a reference to an ldisc * @disc: ldisc number + * @ld: tty line discipline structure to use * * Takes a reference to a line discipline. Deals with refcounts and * module locking counts. Returns NULL if the discipline is not available. @@ -760,32 +797,20 @@ EXPORT_SYMBOL(tty_unregister_ldisc); * takes tty_ldisc_lock to guard against ldisc races */ -struct tty_ldisc *tty_ldisc_get(int disc) +static int tty_ldisc_get(int disc, struct tty_ldisc *ld) { - unsigned long flags; - struct tty_ldisc *ld; + int err; if (disc < N_TTY || disc >= NR_LDISCS) - return NULL; - - spin_lock_irqsave(&tty_ldisc_lock, flags); - - ld = &tty_ldiscs[disc]; - /* Check the entry is defined */ - if (ld->flags & LDISC_FLAG_DEFINED) { - /* If the module is being unloaded we can't use it */ - if (!try_module_get(ld->owner)) - ld = NULL; - else /* lock it */ - ld->refcount++; - } else - ld = NULL; - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - return ld; + return -EINVAL; + err = tty_ldisc_try_get(disc, ld); + if (err == -EAGAIN) { + request_module("tty-ldisc-%d", disc); + err = tty_ldisc_try_get(disc, ld); + } + return err; } -EXPORT_SYMBOL_GPL(tty_ldisc_get); - /** * tty_ldisc_put - drop ldisc reference * @disc: ldisc number @@ -797,22 +822,67 @@ EXPORT_SYMBOL_GPL(tty_ldisc_get); * takes tty_ldisc_lock to guard against ldisc races */ -void tty_ldisc_put(int disc) +static void tty_ldisc_put(struct tty_ldisc_ops *ld) { - struct tty_ldisc *ld; unsigned long flags; + int disc = ld->num; BUG_ON(disc < N_TTY || disc >= NR_LDISCS); spin_lock_irqsave(&tty_ldisc_lock, flags); - ld = &tty_ldiscs[disc]; + ld = tty_ldiscs[disc]; BUG_ON(ld->refcount == 0); ld->refcount--; module_put(ld->owner); spin_unlock_irqrestore(&tty_ldisc_lock, flags); } -EXPORT_SYMBOL_GPL(tty_ldisc_put); +static void * tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos) +{ + return (*pos < NR_LDISCS) ? pos : NULL; +} + +static void * tty_ldiscs_seq_next(struct seq_file *m, void *v, loff_t *pos) +{ + (*pos)++; + return (*pos < NR_LDISCS) ? pos : NULL; +} + +static void tty_ldiscs_seq_stop(struct seq_file *m, void *v) +{ +} + +static int tty_ldiscs_seq_show(struct seq_file *m, void *v) +{ + int i = *(loff_t *)v; + struct tty_ldisc ld; + + if (tty_ldisc_get(i, &ld) < 0) + return 0; + seq_printf(m, "%-10s %2d\n", ld.ops->name ? ld.ops->name : "???", i); + tty_ldisc_put(ld.ops); + return 0; +} + +static const struct seq_operations tty_ldiscs_seq_ops = { + .start = tty_ldiscs_seq_start, + .next = tty_ldiscs_seq_next, + .stop = tty_ldiscs_seq_stop, + .show = tty_ldiscs_seq_show, +}; + +static int proc_tty_ldiscs_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &tty_ldiscs_seq_ops); +} + +const struct file_operations tty_ldiscs_proc_fops = { + .owner = THIS_MODULE, + .open = proc_tty_ldiscs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; /** * tty_ldisc_assign - set ldisc on a tty @@ -829,8 +899,8 @@ EXPORT_SYMBOL_GPL(tty_ldisc_put); static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld) { + ld->refcount = 0; tty->ldisc = *ld; - tty->ldisc.refcount = 0; } /** @@ -953,6 +1023,41 @@ static void tty_ldisc_enable(struct tty_struct *tty) wake_up(&tty_ldisc_wait); } +/** + * tty_ldisc_restore - helper for tty ldisc change + * @tty: tty to recover + * @old: previous ldisc + * + * Restore the previous line discipline or N_TTY when a line discipline + * change fails due to an open error + */ + +static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) +{ + char buf[64]; + struct tty_ldisc new_ldisc; + + /* There is an outstanding reference here so this is safe */ + tty_ldisc_get(old->ops->num, old); + tty_ldisc_assign(tty, old); + tty_set_termios_ldisc(tty, old->ops->num); + if (old->ops->open && (old->ops->open(tty) < 0)) { + tty_ldisc_put(old->ops); + /* This driver is always present */ + if (tty_ldisc_get(N_TTY, &new_ldisc) < 0) + panic("n_tty: get"); + tty_ldisc_assign(tty, &new_ldisc); + tty_set_termios_ldisc(tty, N_TTY); + if (new_ldisc.ops->open) { + int r = new_ldisc.ops->open(tty); + if (r < 0) + panic("Couldn't open N_TTY ldisc for " + "%s --- error %d.", + tty_name(tty, buf), r); + } + } +} + /** * tty_set_ldisc - set line discipline * @tty: the terminal to set @@ -967,28 +1072,18 @@ static void tty_ldisc_enable(struct tty_struct *tty) static int tty_set_ldisc(struct tty_struct *tty, int ldisc) { - int retval = 0; - struct tty_ldisc o_ldisc; - char buf[64]; + int retval; + struct tty_ldisc o_ldisc, new_ldisc; int work; unsigned long flags; - struct tty_ldisc *ld; struct tty_struct *o_tty; - if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS)) - return -EINVAL; - restart: - - ld = tty_ldisc_get(ldisc); - /* Eduardo Blanco */ - /* Cyrus Durgin */ - if (ld == NULL) { - request_module("tty-ldisc-%d", ldisc); - ld = tty_ldisc_get(ldisc); - } - if (ld == NULL) - return -EINVAL; + /* This is a bit ugly for now but means we can break the 'ldisc + is part of the tty struct' assumption later */ + retval = tty_ldisc_get(ldisc, &new_ldisc); + if (retval) + return retval; /* * Problem: What do we do if this blocks ? @@ -996,8 +1091,8 @@ restart: tty_wait_until_sent(tty, 0); - if (tty->ldisc.num == ldisc) { - tty_ldisc_put(ldisc); + if (tty->ldisc.ops->num == ldisc) { + tty_ldisc_put(new_ldisc.ops); return 0; } @@ -1024,7 +1119,7 @@ restart: /* Free the new ldisc we grabbed. Must drop the lock first. */ spin_unlock_irqrestore(&tty_ldisc_lock, flags); - tty_ldisc_put(ldisc); + tty_ldisc_put(o_ldisc.ops); /* * There are several reasons we may be busy, including * random momentary I/O traffic. We must therefore @@ -1038,7 +1133,7 @@ restart: } if (o_tty && o_tty->ldisc.refcount) { spin_unlock_irqrestore(&tty_ldisc_lock, flags); - tty_ldisc_put(ldisc); + tty_ldisc_put(o_tty->ldisc.ops); if (wait_event_interruptible(tty_ldisc_wait, o_tty->ldisc.refcount == 0) < 0) return -ERESTARTSYS; goto restart; @@ -1049,8 +1144,9 @@ restart: * another ldisc change */ if (!test_bit(TTY_LDISC, &tty->flags)) { + struct tty_ldisc *ld; spin_unlock_irqrestore(&tty_ldisc_lock, flags); - tty_ldisc_put(ldisc); + tty_ldisc_put(new_ldisc.ops); ld = tty_ldisc_ref_wait(tty); tty_ldisc_deref(ld); goto restart; @@ -1060,7 +1156,7 @@ restart: if (o_tty) clear_bit(TTY_LDISC, &o_tty->flags); spin_unlock_irqrestore(&tty_ldisc_lock, flags); - + /* * From this point on we know nobody has an ldisc * usage reference, nor can they obtain one until @@ -1070,45 +1166,30 @@ restart: work = cancel_delayed_work(&tty->buf.work); /* * Wait for ->hangup_work and ->buf.work handlers to terminate + * MUST NOT hold locks here. */ flush_scheduled_work(); /* Shutdown the current discipline. */ - if (tty->ldisc.close) - (tty->ldisc.close)(tty); + if (o_ldisc.ops->close) + (o_ldisc.ops->close)(tty); /* Now set up the new line discipline. */ - tty_ldisc_assign(tty, ld); + tty_ldisc_assign(tty, &new_ldisc); tty_set_termios_ldisc(tty, ldisc); - if (tty->ldisc.open) - retval = (tty->ldisc.open)(tty); + if (new_ldisc.ops->open) + retval = (new_ldisc.ops->open)(tty); if (retval < 0) { - tty_ldisc_put(ldisc); - /* There is an outstanding reference here so this is safe */ - tty_ldisc_assign(tty, tty_ldisc_get(o_ldisc.num)); - tty_set_termios_ldisc(tty, tty->ldisc.num); - if (tty->ldisc.open && (tty->ldisc.open(tty) < 0)) { - tty_ldisc_put(o_ldisc.num); - /* This driver is always present */ - tty_ldisc_assign(tty, tty_ldisc_get(N_TTY)); - tty_set_termios_ldisc(tty, N_TTY); - if (tty->ldisc.open) { - int r = tty->ldisc.open(tty); - - if (r < 0) - panic("Couldn't open N_TTY ldisc for " - "%s --- error %d.", - tty_name(tty, buf), r); - } - } + tty_ldisc_put(new_ldisc.ops); + tty_ldisc_restore(tty, &o_ldisc); } /* At this point we hold a reference to the new ldisc and a a reference to the old ldisc. If we ended up flipping back to the existing ldisc we have two references to it */ - if (tty->ldisc.num != o_ldisc.num && tty->ops->set_ldisc) + if (tty->ldisc.ops->num != o_ldisc.ops->num && tty->ops->set_ldisc) tty->ops->set_ldisc(tty); - tty_ldisc_put(o_ldisc.num); + tty_ldisc_put(o_ldisc.ops); /* * Allow ldisc referencing to occur as soon as the driver @@ -1335,8 +1416,8 @@ void tty_wakeup(struct tty_struct *tty) if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) { ld = tty_ldisc_ref(tty); if (ld) { - if (ld->write_wakeup) - ld->write_wakeup(tty); + if (ld->ops->write_wakeup) + ld->ops->write_wakeup(tty); tty_ldisc_deref(ld); } } @@ -1357,8 +1438,8 @@ void tty_ldisc_flush(struct tty_struct *tty) { struct tty_ldisc *ld = tty_ldisc_ref(tty); if (ld) { - if (ld->flush_buffer) - ld->flush_buffer(tty); + if (ld->ops->flush_buffer) + ld->ops->flush_buffer(tty); tty_ldisc_deref(ld); } tty_buffer_flush(tty); @@ -1386,7 +1467,7 @@ static void tty_reset_termios(struct tty_struct *tty) * do_tty_hangup - actual handler for hangup events * @work: tty device * - * This can be called by the "eventd" kernel thread. That is process +k * This can be called by the "eventd" kernel thread. That is process * synchronous but doesn't hold any locks, so we need to make sure we * have the appropriate locks for what we're doing. * @@ -1449,14 +1530,14 @@ static void do_tty_hangup(struct work_struct *work) ld = tty_ldisc_ref(tty); if (ld != NULL) { /* We may have no line discipline at this point */ - if (ld->flush_buffer) - ld->flush_buffer(tty); + if (ld->ops->flush_buffer) + ld->ops->flush_buffer(tty); tty_driver_flush_buffer(tty); if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) && - ld->write_wakeup) - ld->write_wakeup(tty); - if (ld->hangup) - ld->hangup(tty); + ld->ops->write_wakeup) + ld->ops->write_wakeup(tty); + if (ld->ops->hangup) + ld->ops->hangup(tty); } /* * FIXME: Once we trust the LDISC code better we can wait here for @@ -1825,8 +1906,8 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count, /* We want to wait for the line discipline to sort out in this situation */ ld = tty_ldisc_ref_wait(tty); - if (ld->read) - i = (ld->read)(tty, file, buf, count); + if (ld->ops->read) + i = (ld->ops->read)(tty, file, buf, count); else i = -EIO; tty_ldisc_deref(ld); @@ -1978,10 +2059,10 @@ static ssize_t tty_write(struct file *file, const char __user *buf, printk(KERN_ERR "tty driver %s lacks a write_room method.\n", tty->driver->name); ld = tty_ldisc_ref_wait(tty); - if (!ld->write) + if (!ld->ops->write) ret = -EIO; else - ret = do_tty_write(ld->write, tty, file, buf, count); + ret = do_tty_write(ld->ops->write, tty, file, buf, count); tty_ldisc_deref(ld); return ret; } @@ -2076,6 +2157,7 @@ static int init_dev(struct tty_driver *driver, int idx, struct ktermios *tp, **tp_loc, *o_tp, **o_tp_loc; struct ktermios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc; int retval = 0; + struct tty_ldisc *ld; /* check whether we're reopening an existing tty */ if (driver->flags & TTY_DRIVER_DEVPTS_MEM) { @@ -2224,17 +2306,19 @@ static int init_dev(struct tty_driver *driver, int idx, * If we fail here just call release_tty to clean up. No need * to decrement the use counts, as release_tty doesn't care. */ + + ld = &tty->ldisc; - if (tty->ldisc.open) { - retval = (tty->ldisc.open)(tty); + if (ld->ops->open) { + retval = (ld->ops->open)(tty); if (retval) goto release_mem_out; } - if (o_tty && o_tty->ldisc.open) { - retval = (o_tty->ldisc.open)(o_tty); + if (o_tty && o_tty->ldisc.ops->open) { + retval = (o_tty->ldisc.ops->open)(o_tty); if (retval) { - if (tty->ldisc.close) - (tty->ldisc.close)(tty); + if (ld->ops->close) + (ld->ops->close)(tty); goto release_mem_out; } tty_ldisc_enable(o_tty); @@ -2378,6 +2462,7 @@ static void release_tty(struct tty_struct *tty, int idx) static void release_dev(struct file *filp) { struct tty_struct *tty, *o_tty; + struct tty_ldisc ld; int pty_master, tty_closing, o_tty_closing, do_sleep; int devpts; int idx; @@ -2611,26 +2696,27 @@ static void release_dev(struct file *filp) spin_unlock_irqrestore(&tty_ldisc_lock, flags); /* * Shutdown the current line discipline, and reset it to N_TTY. - * N.B. why reset ldisc when we're releasing the memory?? * * FIXME: this MUST get fixed for the new reflocking */ - if (tty->ldisc.close) - (tty->ldisc.close)(tty); - tty_ldisc_put(tty->ldisc.num); + if (tty->ldisc.ops->close) + (tty->ldisc.ops->close)(tty); + tty_ldisc_put(tty->ldisc.ops); /* * Switch the line discipline back */ - tty_ldisc_assign(tty, tty_ldisc_get(N_TTY)); + WARN_ON(tty_ldisc_get(N_TTY, &ld)); + tty_ldisc_assign(tty, &ld); tty_set_termios_ldisc(tty, N_TTY); if (o_tty) { /* FIXME: could o_tty be in setldisc here ? */ clear_bit(TTY_LDISC, &o_tty->flags); - if (o_tty->ldisc.close) - (o_tty->ldisc.close)(o_tty); - tty_ldisc_put(o_tty->ldisc.num); - tty_ldisc_assign(o_tty, tty_ldisc_get(N_TTY)); + if (o_tty->ldisc.ops->close) + (o_tty->ldisc.ops->close)(o_tty); + tty_ldisc_put(o_tty->ldisc.ops); + WARN_ON(tty_ldisc_get(N_TTY, &ld)); + tty_ldisc_assign(o_tty, &ld); tty_set_termios_ldisc(o_tty, N_TTY); } /* @@ -2899,8 +2985,8 @@ static unsigned int tty_poll(struct file *filp, poll_table *wait) return 0; ld = tty_ldisc_ref_wait(tty); - if (ld->poll) - ret = (ld->poll)(tty, filp, wait); + if (ld->ops->poll) + ret = (ld->ops->poll)(tty, filp, wait); tty_ldisc_deref(ld); return ret; } @@ -2974,7 +3060,7 @@ static int tiocsti(struct tty_struct *tty, char __user *p) if (get_user(ch, p)) return -EFAULT; ld = tty_ldisc_ref_wait(tty); - ld->receive_buf(tty, &ch, &mbz, 1); + ld->ops->receive_buf(tty, &ch, &mbz, 1); tty_ldisc_deref(ld); return 0; } @@ -3528,7 +3614,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case TIOCGSID: return tiocgsid(tty, real_tty, p); case TIOCGETD: - return put_user(tty->ldisc.num, (int __user *)p); + return put_user(tty->ldisc.ops->num, (int __user *)p); case TIOCSETD: return tiocsetd(tty, p); #ifdef CONFIG_VT @@ -3581,8 +3667,8 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } ld = tty_ldisc_ref_wait(tty); retval = -EINVAL; - if (ld->ioctl) { - retval = ld->ioctl(tty, file, cmd, arg); + if (ld->ops->ioctl) { + retval = ld->ops->ioctl(tty, file, cmd, arg); if (retval == -ENOIOCTLCMD) retval = -EINVAL; } @@ -3609,8 +3695,8 @@ static long tty_compat_ioctl(struct file *file, unsigned int cmd, } ld = tty_ldisc_ref_wait(tty); - if (ld->compat_ioctl) - retval = ld->compat_ioctl(tty, file, cmd, arg); + if (ld->ops->compat_ioctl) + retval = ld->ops->compat_ioctl(tty, file, cmd, arg); tty_ldisc_deref(ld); return retval; @@ -3782,7 +3868,8 @@ static void flush_to_ldisc(struct work_struct *work) flag_buf = head->flag_buf_ptr + head->read; head->read += count; spin_unlock_irqrestore(&tty->buf.lock, flags); - disc->receive_buf(tty, char_buf, flag_buf, count); + disc->ops->receive_buf(tty, char_buf, + flag_buf, count); spin_lock_irqsave(&tty->buf.lock, flags); } /* Restore the queue head */ @@ -3843,9 +3930,12 @@ EXPORT_SYMBOL(tty_flip_buffer_push); static void initialize_tty_struct(struct tty_struct *tty) { + struct tty_ldisc ld; memset(tty, 0, sizeof(struct tty_struct)); tty->magic = TTY_MAGIC; - tty_ldisc_assign(tty, tty_ldisc_get(N_TTY)); + if (tty_ldisc_get(N_TTY, &ld) < 0) + panic("n_tty: init_tty"); + tty_ldisc_assign(tty, &ld); tty->session = NULL; tty->pgrp = NULL; tty->overrun_time = jiffies; diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c index 8f81139d6194..ea9fc5d03b99 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/char/tty_ioctl.c @@ -491,8 +491,8 @@ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios) ld = tty_ldisc_ref(tty); if (ld != NULL) { - if (ld->set_termios) - (ld->set_termios)(tty, &old_termios); + if (ld->ops->set_termios) + (ld->ops->set_termios)(tty, &old_termios); tty_ldisc_deref(ld); } mutex_unlock(&tty->termios_mutex); @@ -552,8 +552,8 @@ static int set_termios(struct tty_struct *tty, void __user *arg, int opt) ld = tty_ldisc_ref(tty); if (ld != NULL) { - if ((opt & TERMIOS_FLUSH) && ld->flush_buffer) - ld->flush_buffer(tty); + if ((opt & TERMIOS_FLUSH) && ld->ops->flush_buffer) + ld->ops->flush_buffer(tty); tty_ldisc_deref(ld); } @@ -959,12 +959,12 @@ int tty_perform_flush(struct tty_struct *tty, unsigned long arg) ld = tty_ldisc_ref(tty); switch (arg) { case TCIFLUSH: - if (ld && ld->flush_buffer) - ld->flush_buffer(tty); + if (ld && ld->ops->flush_buffer) + ld->ops->flush_buffer(tty); break; case TCIOFLUSH: - if (ld && ld->flush_buffer) - ld->flush_buffer(tty); + if (ld && ld->ops->flush_buffer) + ld->ops->flush_buffer(tty); /* fall through */ case TCOFLUSH: tty_driver_flush_buffer(tty); diff --git a/drivers/input/serio/serport.c b/drivers/input/serio/serport.c index 7ff71ba7b7c9..b9694b6445d0 100644 --- a/drivers/input/serio/serport.c +++ b/drivers/input/serio/serport.c @@ -216,7 +216,7 @@ static void serport_ldisc_write_wakeup(struct tty_struct * tty) * The line discipline structure. */ -static struct tty_ldisc serport_ldisc = { +static struct tty_ldisc_ops serport_ldisc = { .owner = THIS_MODULE, .name = "input", .open = serport_ldisc_open, diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c index 2095153582f1..8a35029caca0 100644 --- a/drivers/isdn/capi/capi.c +++ b/drivers/isdn/capi/capi.c @@ -466,7 +466,7 @@ static int handle_recv_skb(struct capiminor *mp, struct sk_buff *skb) ld = tty_ldisc_ref(mp->tty); if (ld == NULL) return -1; - if (ld->receive_buf == NULL) { + if (ld->ops->receive_buf == NULL) { #if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) printk(KERN_DEBUG "capi: ldisc has no receive_buf function\n"); #endif @@ -501,7 +501,7 @@ static int handle_recv_skb(struct capiminor *mp, struct sk_buff *skb) printk(KERN_DEBUG "capi: DATA_B3_RESP %u len=%d => ldisc\n", datahandle, skb->len); #endif - ld->receive_buf(mp->tty, skb->data, NULL, skb->len); + ld->ops->receive_buf(mp->tty, skb->data, NULL, skb->len); kfree_skb(skb); tty_ldisc_deref(ld); return 0; diff --git a/drivers/isdn/gigaset/ser-gigaset.c b/drivers/isdn/gigaset/ser-gigaset.c index 45d1ee93cd39..5e89fa177816 100644 --- a/drivers/isdn/gigaset/ser-gigaset.c +++ b/drivers/isdn/gigaset/ser-gigaset.c @@ -766,7 +766,7 @@ gigaset_tty_wakeup(struct tty_struct *tty) cs_put(cs); } -static struct tty_ldisc gigaset_ldisc = { +static struct tty_ldisc_ops gigaset_ldisc = { .owner = THIS_MODULE, .magic = TTY_LDISC_MAGIC, .name = "ser_gigaset", diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c index 9d5721287d6f..19dd0a61749c 100644 --- a/drivers/net/hamradio/6pack.c +++ b/drivers/net/hamradio/6pack.c @@ -783,7 +783,7 @@ static int sixpack_ioctl(struct tty_struct *tty, struct file *file, return err; } -static struct tty_ldisc sp_ldisc = { +static struct tty_ldisc_ops sp_ldisc = { .owner = THIS_MODULE, .magic = TTY_LDISC_MAGIC, .name = "6pack", diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c index 65166035aca0..c6ca47599fd4 100644 --- a/drivers/net/hamradio/mkiss.c +++ b/drivers/net/hamradio/mkiss.c @@ -969,7 +969,7 @@ out: mkiss_put(ax); } -static struct tty_ldisc ax_ldisc = { +static struct tty_ldisc_ops ax_ldisc = { .owner = THIS_MODULE, .magic = TTY_LDISC_MAGIC, .name = "mkiss", diff --git a/drivers/net/irda/irtty-sir.c b/drivers/net/irda/irtty-sir.c index e6f40b7f9041..9e33196f9459 100644 --- a/drivers/net/irda/irtty-sir.c +++ b/drivers/net/irda/irtty-sir.c @@ -533,7 +533,7 @@ static void irtty_close(struct tty_struct *tty) /* ------------------------------------------------------- */ -static struct tty_ldisc irda_ldisc = { +static struct tty_ldisc_ops irda_ldisc = { .magic = TTY_LDISC_MAGIC, .name = "irda", .flags = 0, diff --git a/drivers/net/ppp_async.c b/drivers/net/ppp_async.c index f1a52def1241..451bdb57d6fc 100644 --- a/drivers/net/ppp_async.c +++ b/drivers/net/ppp_async.c @@ -378,7 +378,7 @@ ppp_asynctty_wakeup(struct tty_struct *tty) } -static struct tty_ldisc ppp_ldisc = { +static struct tty_ldisc_ops ppp_ldisc = { .owner = THIS_MODULE, .magic = TTY_LDISC_MAGIC, .name = "ppp", diff --git a/drivers/net/ppp_synctty.c b/drivers/net/ppp_synctty.c index b8f0369a71e7..801d8f99d471 100644 --- a/drivers/net/ppp_synctty.c +++ b/drivers/net/ppp_synctty.c @@ -418,7 +418,7 @@ ppp_sync_wakeup(struct tty_struct *tty) } -static struct tty_ldisc ppp_sync_ldisc = { +static struct tty_ldisc_ops ppp_sync_ldisc = { .owner = THIS_MODULE, .magic = TTY_LDISC_MAGIC, .name = "pppsync", diff --git a/drivers/net/slip.c b/drivers/net/slip.c index 84af68fdb6c2..1d58991d395b 100644 --- a/drivers/net/slip.c +++ b/drivers/net/slip.c @@ -1301,7 +1301,7 @@ static int sl_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) #endif /* VSV changes end */ -static struct tty_ldisc sl_ldisc = { +static struct tty_ldisc_ops sl_ldisc = { .owner = THIS_MODULE, .magic = TTY_LDISC_MAGIC, .name = "slip", diff --git a/drivers/net/wan/pc300_tty.c b/drivers/net/wan/pc300_tty.c index e03eef2f2282..c2c10c632260 100644 --- a/drivers/net/wan/pc300_tty.c +++ b/drivers/net/wan/pc300_tty.c @@ -688,9 +688,9 @@ static void cpc_tty_rx_work(struct work_struct *work) if (cpc_tty->tty) { ld = tty_ldisc_ref(cpc_tty->tty); if (ld) { - if (ld->receive_buf) { + if (ld->ops->receive_buf) { CPC_TTY_DBG("%s: call line disc. receive_buf\n",cpc_tty->name); - ld->receive_buf(cpc_tty->tty, (char *)(buf->data), &flags, buf->size); + ld->ops->receive_buf(cpc_tty->tty, (char *)(buf->data), &flags, buf->size); } tty_ldisc_deref(ld); } diff --git a/drivers/net/wan/x25_asy.c b/drivers/net/wan/x25_asy.c index 069f8bb0a99f..2a6c7a60756f 100644 --- a/drivers/net/wan/x25_asy.c +++ b/drivers/net/wan/x25_asy.c @@ -754,7 +754,7 @@ static void x25_asy_setup(struct net_device *dev) dev->flags = IFF_NOARP; } -static struct tty_ldisc x25_ldisc = { +static struct tty_ldisc_ops x25_ldisc = { .owner = THIS_MODULE, .magic = TTY_LDISC_MAGIC, .name = "X.25", diff --git a/fs/proc/proc_tty.c b/fs/proc/proc_tty.c index 21f490f5d65c..d153946d6d15 100644 --- a/fs/proc/proc_tty.c +++ b/fs/proc/proc_tty.c @@ -136,54 +136,6 @@ static const struct file_operations proc_tty_drivers_operations = { .release = seq_release, }; -static void * tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos) -{ - return (*pos < NR_LDISCS) ? pos : NULL; -} - -static void * tty_ldiscs_seq_next(struct seq_file *m, void *v, loff_t *pos) -{ - (*pos)++; - return (*pos < NR_LDISCS) ? pos : NULL; -} - -static void tty_ldiscs_seq_stop(struct seq_file *m, void *v) -{ -} - -static int tty_ldiscs_seq_show(struct seq_file *m, void *v) -{ - int i = *(loff_t *)v; - struct tty_ldisc *ld; - - ld = tty_ldisc_get(i); - if (ld == NULL) - return 0; - seq_printf(m, "%-10s %2d\n", ld->name ? ld->name : "???", i); - tty_ldisc_put(i); - return 0; -} - -static const struct seq_operations tty_ldiscs_seq_ops = { - .start = tty_ldiscs_seq_start, - .next = tty_ldiscs_seq_next, - .stop = tty_ldiscs_seq_stop, - .show = tty_ldiscs_seq_show, -}; - -static int proc_tty_ldiscs_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &tty_ldiscs_seq_ops); -} - -static const struct file_operations tty_ldiscs_proc_fops = { - .owner = THIS_MODULE, - .open = proc_tty_ldiscs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - /* * This function is called by tty_register_driver() to handle * registering the driver's /proc handler into /proc/tty/driver/ diff --git a/include/linux/tty.h b/include/linux/tty.h index 324a3b231d40..013711ea7385 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -185,6 +185,7 @@ struct tty_struct { struct tty_driver *driver; const struct tty_operations *ops; int index; + /* The ldisc objects are protected by tty_ldisc_lock at the moment */ struct tty_ldisc ldisc; struct mutex termios_mutex; spinlock_t ctrl_lock; @@ -289,7 +290,7 @@ extern void tty_wait_until_sent(struct tty_struct * tty, long timeout); extern int tty_check_change(struct tty_struct * tty); extern void stop_tty(struct tty_struct * tty); extern void start_tty(struct tty_struct * tty); -extern int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc); +extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc); extern int tty_unregister_ldisc(int disc); extern int tty_register_driver(struct tty_driver *driver); extern int tty_unregister_driver(struct tty_driver *driver); @@ -330,9 +331,7 @@ extern int tty_termios_hw_change(struct ktermios *a, struct ktermios *b); extern struct tty_ldisc *tty_ldisc_ref(struct tty_struct *); extern void tty_ldisc_deref(struct tty_ldisc *); extern struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *); - -extern struct tty_ldisc *tty_ldisc_get(int); -extern void tty_ldisc_put(int); +extern const struct file_operations tty_ldiscs_proc_fops; extern void tty_wakeup(struct tty_struct *tty); extern void tty_ldisc_flush(struct tty_struct *tty); @@ -354,7 +353,7 @@ extern int tty_write_lock(struct tty_struct *tty, int ndelay); /* n_tty.c */ -extern struct tty_ldisc tty_ldisc_N_TTY; +extern struct tty_ldisc_ops tty_ldisc_N_TTY; /* tty_audit.c */ #ifdef CONFIG_AUDIT diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h index 6226504d9108..40f38d896777 100644 --- a/include/linux/tty_ldisc.h +++ b/include/linux/tty_ldisc.h @@ -104,7 +104,7 @@ #include #include -struct tty_ldisc { +struct tty_ldisc_ops { int magic; char *name; int num; @@ -142,6 +142,11 @@ struct tty_ldisc { int refcount; }; +struct tty_ldisc { + struct tty_ldisc_ops *ops; + int refcount; +}; + #define TTY_LDISC_MAGIC 0x5403 #define LDISC_FLAG_DEFINED 0x00000001 diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index c9191871c1e0..0a387f2eb7a9 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -617,14 +617,7 @@ static void rfcomm_tty_wakeup(unsigned long arg) return; BT_DBG("dev %p tty %p", dev, tty); - - if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); - - wake_up_interruptible(&tty->write_wait); -#ifdef SERIAL_HAVE_POLL_WAIT - wake_up_interruptible(&tty->poll_wait); -#endif + tty_wakeup(tty); } static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp) @@ -1005,9 +998,7 @@ static void rfcomm_tty_flush_buffer(struct tty_struct *tty) return; skb_queue_purge(&dev->dlc->tx_queue); - - if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && tty->ldisc.write_wakeup) - tty->ldisc.write_wakeup(tty); + tty_wakeup(tty); } static void rfcomm_tty_send_xchar(struct tty_struct *tty, char ch) diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c index 76c3057d0179..e4e2caeb9d82 100644 --- a/net/irda/ircomm/ircomm_tty.c +++ b/net/irda/ircomm/ircomm_tty.c @@ -650,12 +650,7 @@ static void ircomm_tty_do_softint(struct work_struct *work) } /* Check if user (still) wants to be waken up */ - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - { - (tty->ldisc.write_wakeup)(tty); - } - wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); } /* @@ -1141,6 +1136,7 @@ static int ircomm_tty_data_indication(void *instance, void *sap, struct sk_buff *skb) { struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; + struct tty_ldisc *ld; IRDA_DEBUG(2, "%s()\n", __func__ ); @@ -1173,7 +1169,11 @@ static int ircomm_tty_data_indication(void *instance, void *sap, * involve the flip buffers, since we are not running in an interrupt * handler */ - self->tty->ldisc.receive_buf(self->tty, skb->data, NULL, skb->len); + + ld = tty_ldisc_ref(self->tty); + if (ld) + ld->ops->receive_buf(self->tty, skb->data, NULL, skb->len); + tty_ldisc_deref(ld); /* No need to kfree_skb - see ircomm_ttp_data_indication() */ -- cgit v1.2.3 From 6f67048cd010afe19d79d821f16055d9c704c6f0 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 16 Jul 2008 21:53:41 +0100 Subject: tty: Introduce a tty_port common structure Every tty driver has its own concept of a port structure and because they all differ we cannot extract commonality. Begin fixing this by creating a structure drivers can elect to use so that over time we can push fields into this and create commonality and then introduce common methods. Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/tty_io.c | 34 ++++++++++++++++++++++++++++++++++ include/linux/tty.h | 30 +++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 54c4ada460ee..739c9c59fc62 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -2088,6 +2088,40 @@ ssize_t redirected_tty_write(struct file *file, const char __user *buf, return tty_write(file, buf, count, ppos); } +void tty_port_init(struct tty_port *port) +{ + memset(port, 0, sizeof(*port)); + init_waitqueue_head(&port->open_wait); + init_waitqueue_head(&port->close_wait); + mutex_init(&port->mutex); +} +EXPORT_SYMBOL(tty_port_init); + +int tty_port_alloc_xmit_buf(struct tty_port *port) +{ + /* We may sleep in get_zeroed_page() */ + mutex_lock(&port->mutex); + if (port->xmit_buf == NULL) + port->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL); + mutex_unlock(&port->mutex); + if (port->xmit_buf == NULL) + return -ENOMEM; + return 0; +} +EXPORT_SYMBOL(tty_port_alloc_xmit_buf); + +void tty_port_free_xmit_buf(struct tty_port *port) +{ + mutex_lock(&port->mutex); + if (port->xmit_buf != NULL) { + free_page((unsigned long)port->xmit_buf); + port->xmit_buf = NULL; + } + mutex_unlock(&port->mutex); +} +EXPORT_SYMBOL(tty_port_free_xmit_buf); + + static char ptychar[] = "pqrstuvwxyzabcde"; /** diff --git a/include/linux/tty.h b/include/linux/tty.h index 013711ea7385..d7c695b65c0e 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -166,6 +166,29 @@ struct tty_bufhead { struct device; struct signal_struct; + +/* + * Port level information. Each device keeps its own port level information + * so provide a common structure for those ports wanting to use common support + * routines. + * + * The tty port has a different lifetime to the tty so must be kept apart. + * In addition be careful as tty -> port mappings are valid for the life + * of the tty object but in many cases port -> tty mappings are valid only + * until a hangup so don't use the wrong path. + */ + +struct tty_port { + struct tty_struct *tty; /* Back pointer */ + int blocked_open; /* Waiting to open */ + int count; /* Usage count */ + wait_queue_head_t open_wait; /* Open waiters */ + wait_queue_head_t close_wait; /* Close waiters */ + unsigned long flags; /* TTY flags ASY_*/ + struct mutex mutex; /* Locking */ + unsigned char *xmit_buf; /* Optional buffer */ +}; + /* * Where all of the state associated with a tty is kept while the tty * is open. Since the termios state should be kept even if the tty @@ -214,7 +237,7 @@ struct tty_struct { struct list_head tty_files; #define N_TTY_BUF_SIZE 4096 - + /* * The following is data for the N_TTY line discipline. For * historical reasons, this is included in the tty structure. @@ -242,6 +265,7 @@ struct tty_struct { spinlock_t read_lock; /* If the tty has a pending do_SAK, queue it here - akpm */ struct work_struct SAK_work; + struct tty_port *port; }; /* tty magic number */ @@ -350,6 +374,10 @@ extern void tty_write_unlock(struct tty_struct *tty); extern int tty_write_lock(struct tty_struct *tty, int ndelay); #define tty_is_writelocked(tty) (mutex_is_locked(&tty->atomic_write_lock)) +extern void tty_port_init(struct tty_port *port); +extern int tty_port_alloc_xmit_buf(struct tty_port *port); +extern void tty_port_free_xmit_buf(struct tty_port *port); + /* n_tty.c */ -- cgit v1.2.3 From df4f4dd429870f435f8d5d9d561db029a29f063b Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 16 Jul 2008 21:53:50 +0100 Subject: serial: use tty_port Switch the serial_core based drivers to use the new tty_port structure. We can't quite use all of it yet because of the dynamically allocated extras in the serial_core layer. Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/serial/8250.c | 2 +- drivers/serial/jsm/jsm_neo.c | 2 +- drivers/serial/jsm/jsm_tty.c | 8 ++--- drivers/serial/serial_core.c | 80 +++++++++++++++++++++++--------------------- include/linux/serial_core.h | 26 +++++++------- 5 files changed, 60 insertions(+), 58 deletions(-) (limited to 'include/linux') diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index ac4f20cbfe41..ce948b66bbd4 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -1287,7 +1287,7 @@ static void serial8250_enable_ms(struct uart_port *port) static void receive_chars(struct uart_8250_port *up, unsigned int *status) { - struct tty_struct *tty = up->port.info->tty; + struct tty_struct *tty = up->port.info->port.tty; unsigned char ch, lsr = *status; int max_count = 256; char flag; diff --git a/drivers/serial/jsm/jsm_neo.c b/drivers/serial/jsm/jsm_neo.c index b2d6f5b1a7c2..b7584ca55ade 100644 --- a/drivers/serial/jsm/jsm_neo.c +++ b/drivers/serial/jsm/jsm_neo.c @@ -998,7 +998,7 @@ static void neo_param(struct jsm_channel *ch) { 50, B50 }, }; - cflag = C_BAUD(ch->uart_port.info->tty); + cflag = C_BAUD(ch->uart_port.info->port.tty); baud = 9600; for (i = 0; i < ARRAY_SIZE(baud_rates); i++) { if (baud_rates[i].cflag == cflag) { diff --git a/drivers/serial/jsm/jsm_tty.c b/drivers/serial/jsm/jsm_tty.c index 94ec66372508..a697914ae3d0 100644 --- a/drivers/serial/jsm/jsm_tty.c +++ b/drivers/serial/jsm/jsm_tty.c @@ -145,7 +145,7 @@ static void jsm_tty_send_xchar(struct uart_port *port, char ch) struct ktermios *termios; spin_lock_irqsave(&port->lock, lock_flags); - termios = port->info->tty->termios; + termios = port->info->port.tty->termios; if (ch == termios->c_cc[VSTART]) channel->ch_bd->bd_ops->send_start_character(channel); @@ -239,7 +239,7 @@ static int jsm_tty_open(struct uart_port *port) channel->ch_cached_lsr = 0; channel->ch_stops_sent = 0; - termios = port->info->tty->termios; + termios = port->info->port.tty->termios; channel->ch_c_cflag = termios->c_cflag; channel->ch_c_iflag = termios->c_iflag; channel->ch_c_oflag = termios->c_oflag; @@ -272,7 +272,7 @@ static void jsm_tty_close(struct uart_port *port) jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev, "start\n"); bd = channel->ch_bd; - ts = channel->uart_port.info->tty->termios; + ts = channel->uart_port.info->port.tty->termios; channel->ch_flags &= ~(CH_STOPI); @@ -515,7 +515,7 @@ void jsm_input(struct jsm_channel *ch) if (!ch) return; - tp = ch->uart_port.info->tty; + tp = ch->uart_port.info->port.tty; bd = ch->ch_bd; if(!bd) diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index 9884bc9eecb1..0bce1fe2c62a 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -50,7 +50,7 @@ static struct lock_class_key port_lock_key; #define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) -#define uart_users(state) ((state)->count + ((state)->info ? (state)->info->blocked_open : 0)) +#define uart_users(state) ((state)->count + ((state)->info ? (state)->info->port.blocked_open : 0)) #ifdef CONFIG_SERIAL_CORE_CONSOLE #define uart_console(port) ((port)->cons && (port)->cons->index == (port)->line) @@ -113,7 +113,7 @@ static void uart_start(struct tty_struct *tty) static void uart_tasklet_action(unsigned long data) { struct uart_state *state = (struct uart_state *)data; - tty_wakeup(state->info->tty); + tty_wakeup(state->info->port.tty); } static inline void @@ -135,7 +135,7 @@ uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear) /* * Startup the port. This will be called once per open. All calls - * will be serialised by the per-port semaphore. + * will be serialised by the per-port mutex. */ static int uart_startup(struct uart_state *state, int init_hw) { @@ -152,7 +152,7 @@ static int uart_startup(struct uart_state *state, int init_hw) * once we have successfully opened the port. Also set * up the tty->alt_speed kludge */ - set_bit(TTY_IO_ERROR, &info->tty->flags); + set_bit(TTY_IO_ERROR, &info->port.tty->flags); if (port->type == PORT_UNKNOWN) return 0; @@ -162,6 +162,7 @@ static int uart_startup(struct uart_state *state, int init_hw) * buffer. */ if (!info->xmit.buf) { + /* This is protected by the per port mutex */ page = get_zeroed_page(GFP_KERNEL); if (!page) return -ENOMEM; @@ -182,20 +183,20 @@ static int uart_startup(struct uart_state *state, int init_hw) * Setup the RTS and DTR signals once the * port is open and ready to respond. */ - if (info->tty->termios->c_cflag & CBAUD) + if (info->port.tty->termios->c_cflag & CBAUD) uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR); } if (info->flags & UIF_CTS_FLOW) { spin_lock_irq(&port->lock); if (!(port->ops->get_mctrl(port) & TIOCM_CTS)) - info->tty->hw_stopped = 1; + info->port.tty->hw_stopped = 1; spin_unlock_irq(&port->lock); } info->flags |= UIF_INITIALIZED; - clear_bit(TTY_IO_ERROR, &info->tty->flags); + clear_bit(TTY_IO_ERROR, &info->port.tty->flags); } if (retval && capable(CAP_SYS_ADMIN)) @@ -217,8 +218,8 @@ static void uart_shutdown(struct uart_state *state) /* * Set the TTY IO error marker */ - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); + if (info->port.tty) + set_bit(TTY_IO_ERROR, &info->port.tty->flags); if (info->flags & UIF_INITIALIZED) { info->flags &= ~UIF_INITIALIZED; @@ -226,7 +227,7 @@ static void uart_shutdown(struct uart_state *state) /* * Turn off DTR and RTS early. */ - if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) + if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL)) uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); /* @@ -426,7 +427,7 @@ EXPORT_SYMBOL(uart_get_divisor); static void uart_change_speed(struct uart_state *state, struct ktermios *old_termios) { - struct tty_struct *tty = state->info->tty; + struct tty_struct *tty = state->info->port.tty; struct uart_port *port = state->port; struct ktermios *termios; @@ -836,8 +837,8 @@ static int uart_set_info(struct uart_state *state, state->closing_wait = closing_wait; if (new_serial.xmit_fifo_size) port->fifosize = new_serial.xmit_fifo_size; - if (state->info->tty) - state->info->tty->low_latency = + if (state->info->port.tty) + state->info->port.tty->low_latency = (port->flags & UPF_LOW_LATENCY) ? 1 : 0; check_and_exit: @@ -857,7 +858,7 @@ static int uart_set_info(struct uart_state *state, printk(KERN_NOTICE "%s sets custom speed on %s. This " "is deprecated.\n", current->comm, - tty_name(state->info->tty, buf)); + tty_name(state->info->port.tty, buf)); } uart_change_speed(state, NULL); } @@ -889,7 +890,7 @@ static int uart_get_lsr_info(struct uart_state *state, */ if (port->x_char || ((uart_circ_chars_pending(&state->info->xmit) > 0) && - !state->info->tty->stopped && !state->info->tty->hw_stopped)) + !state->info->port.tty->stopped && !state->info->port.tty->hw_stopped)) result &= ~TIOCSER_TEMT; return put_user(result, value); @@ -1239,7 +1240,7 @@ static void uart_set_termios(struct tty_struct *tty, */ if (!(old_termios->c_cflag & CLOCAL) && (tty->termios->c_cflag & CLOCAL)) - wake_up_interruptible(&state->info->open_wait); + wake_up_interruptible(&state->info->port.open_wait); #endif } @@ -1320,9 +1321,9 @@ static void uart_close(struct tty_struct *tty, struct file *filp) tty_ldisc_flush(tty); tty->closing = 0; - state->info->tty = NULL; + state->info->port.tty = NULL; - if (state->info->blocked_open) { + if (state->info->port.blocked_open) { if (state->close_delay) msleep_interruptible(state->close_delay); } else if (!uart_console(port)) { @@ -1333,7 +1334,7 @@ static void uart_close(struct tty_struct *tty, struct file *filp) * Wake up anyone trying to open this port. */ state->info->flags &= ~UIF_NORMAL_ACTIVE; - wake_up_interruptible(&state->info->open_wait); + wake_up_interruptible(&state->info->port.open_wait); done: mutex_unlock(&state->mutex); @@ -1417,8 +1418,8 @@ static void uart_hangup(struct tty_struct *tty) uart_shutdown(state); state->count = 0; state->info->flags &= ~UIF_NORMAL_ACTIVE; - state->info->tty = NULL; - wake_up_interruptible(&state->info->open_wait); + state->info->port.tty = NULL; + wake_up_interruptible(&state->info->port.open_wait); wake_up_interruptible(&state->info->delta_msr_wait); } mutex_unlock(&state->mutex); @@ -1432,7 +1433,7 @@ static void uart_hangup(struct tty_struct *tty) */ static void uart_update_termios(struct uart_state *state) { - struct tty_struct *tty = state->info->tty; + struct tty_struct *tty = state->info->port.tty; struct uart_port *port = state->port; if (uart_console(port) && port->cons->cflag) { @@ -1471,17 +1472,17 @@ uart_block_til_ready(struct file *filp, struct uart_state *state) struct uart_port *port = state->port; unsigned int mctrl; - info->blocked_open++; + info->port.blocked_open++; state->count--; - add_wait_queue(&info->open_wait, &wait); + add_wait_queue(&info->port.open_wait, &wait); while (1) { set_current_state(TASK_INTERRUPTIBLE); /* * If we have been hung up, tell userspace/restart open. */ - if (tty_hung_up_p(filp) || info->tty == NULL) + if (tty_hung_up_p(filp) || info->port.tty == NULL) break; /* @@ -1500,8 +1501,8 @@ uart_block_til_ready(struct file *filp, struct uart_state *state) * have set TTY_IO_ERROR for a non-existant port. */ if ((filp->f_flags & O_NONBLOCK) || - (info->tty->termios->c_cflag & CLOCAL) || - (info->tty->flags & (1 << TTY_IO_ERROR))) + (info->port.tty->termios->c_cflag & CLOCAL) || + (info->port.tty->flags & (1 << TTY_IO_ERROR))) break; /* @@ -1509,7 +1510,7 @@ uart_block_til_ready(struct file *filp, struct uart_state *state) * not set RTS here - we want to make sure we catch * the data from the modem. */ - if (info->tty->termios->c_cflag & CBAUD) + if (info->port.tty->termios->c_cflag & CBAUD) uart_set_mctrl(port, TIOCM_DTR); /* @@ -1531,15 +1532,15 @@ uart_block_til_ready(struct file *filp, struct uart_state *state) break; } set_current_state(TASK_RUNNING); - remove_wait_queue(&info->open_wait, &wait); + remove_wait_queue(&info->port.open_wait, &wait); state->count++; - info->blocked_open--; + info->port.blocked_open--; if (signal_pending(current)) return -ERESTARTSYS; - if (!info->tty || tty_hung_up_p(filp)) + if (!info->port.tty || tty_hung_up_p(filp)) return -EAGAIN; return 0; @@ -1562,10 +1563,13 @@ static struct uart_state *uart_get(struct uart_driver *drv, int line) goto err_unlock; } + /* BKL: RACE HERE - LEAK */ + /* We should move this into the uart_state structure and kill off + this whole complexity */ if (!state->info) { state->info = kzalloc(sizeof(struct uart_info), GFP_KERNEL); if (state->info) { - init_waitqueue_head(&state->info->open_wait); + init_waitqueue_head(&state->info->port.open_wait); init_waitqueue_head(&state->info->delta_msr_wait); /* @@ -1622,7 +1626,7 @@ static int uart_open(struct tty_struct *tty, struct file *filp) * be re-entered while allocating the info structure, or while we * request any IRQs that the driver may need. This also has the nice * side-effect that it delays the action of uart_hangup, so we can - * guarantee that info->tty will always contain something reasonable. + * guarantee that info->port.tty will always contain something reasonable. */ state = uart_get(drv, line); if (IS_ERR(state)) { @@ -1638,7 +1642,7 @@ static int uart_open(struct tty_struct *tty, struct file *filp) tty->driver_data = state; tty->low_latency = (state->port->flags & UPF_LOW_LATENCY) ? 1 : 0; tty->alt_speed = 0; - state->info->tty = tty; + state->info->port.tty = tty; /* * If the port is in the middle of closing, bail out now. @@ -2101,8 +2105,8 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *port) /* * If that's unset, use the tty termios setting. */ - if (state->info && state->info->tty && termios.c_cflag == 0) - termios = *state->info->tty->termios; + if (state->info && state->info->port.tty && termios.c_cflag == 0) + termios = *state->info->port.tty->termios; uart_change_pm(state, 0); port->ops->set_termios(port, &termios, NULL); @@ -2521,8 +2525,8 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) tty_unregister_device(drv->tty_driver, port->line); info = state->info; - if (info && info->tty) - tty_vhangup(info->tty); + if (info && info->port.tty) + tty_vhangup(info->port.tty); /* * All users of this port should now be disconnected from diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 1d2faa6592ae..f3a1c0e45021 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -344,13 +344,15 @@ typedef unsigned int __bitwise__ uif_t; * stuff here. */ struct uart_info { - struct tty_struct *tty; + struct tty_port port; struct circ_buf xmit; uif_t flags; /* * Definitions for info->flags. These are _private_ to serial_core, and * are specific to this structure. They may be queried by low level drivers. + * + * FIXME: use the ASY_ definitions */ #define UIF_CHECK_CD ((__force uif_t) (1 << 25)) #define UIF_CTS_FLOW ((__force uif_t) (1 << 26)) @@ -358,11 +360,7 @@ struct uart_info { #define UIF_INITIALIZED ((__force uif_t) (1 << 31)) #define UIF_SUSPENDED ((__force uif_t) (1 << 30)) - int blocked_open; - struct tasklet_struct tlet; - - wait_queue_head_t open_wait; wait_queue_head_t delta_msr_wait; }; @@ -439,8 +437,8 @@ int uart_resume_port(struct uart_driver *reg, struct uart_port *port); #define uart_circ_chars_free(circ) \ (CIRC_SPACE((circ)->head, (circ)->tail, UART_XMIT_SIZE)) -#define uart_tx_stopped(port) \ - ((port)->info->tty->stopped || (port)->info->tty->hw_stopped) +#define uart_tx_stopped(portp) \ + ((portp)->info->port.tty->stopped || (portp)->info->port.tty->hw_stopped) /* * The following are helper functions for the low level drivers. @@ -451,7 +449,7 @@ uart_handle_sysrq_char(struct uart_port *port, unsigned int ch) #ifdef SUPPORT_SYSRQ if (port->sysrq) { if (ch && time_before(jiffies, port->sysrq)) { - handle_sysrq(ch, port->info ? port->info->tty : NULL); + handle_sysrq(ch, port->info ? port->info->port.tty : NULL); port->sysrq = 0; return 1; } @@ -480,7 +478,7 @@ static inline int uart_handle_break(struct uart_port *port) } #endif if (port->flags & UPF_SAK) - do_SAK(info->tty); + do_SAK(info->port.tty); return 0; } @@ -503,9 +501,9 @@ uart_handle_dcd_change(struct uart_port *port, unsigned int status) if (info->flags & UIF_CHECK_CD) { if (status) - wake_up_interruptible(&info->open_wait); - else if (info->tty) - tty_hangup(info->tty); + wake_up_interruptible(&info->port.open_wait); + else if (info->port.tty) + tty_hangup(info->port.tty); } } @@ -518,7 +516,7 @@ static inline void uart_handle_cts_change(struct uart_port *port, unsigned int status) { struct uart_info *info = port->info; - struct tty_struct *tty = info->tty; + struct tty_struct *tty = info->port.tty; port->icount.cts++; @@ -544,7 +542,7 @@ static inline void uart_insert_char(struct uart_port *port, unsigned int status, unsigned int overrun, unsigned int ch, unsigned int flag) { - struct tty_struct *tty = port->info->tty; + struct tty_struct *tty = port->info->port.tty; if ((status & port->ignore_status_mask & ~overrun) == 0) tty_insert_flip_char(tty, ch, flag); -- cgit v1.2.3 From 7a4d29f426f17479395980ded8fa5e3bdd6d94e4 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 16 Jul 2008 21:54:54 +0100 Subject: tty.h: clean up Coding style clean up and white space tidy Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- include/linux/tty.h | 165 +++++++++++++++++++++++++++------------------------- 1 file changed, 85 insertions(+), 80 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tty.h b/include/linux/tty.h index d7c695b65c0e..46008e86b191 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -71,7 +71,8 @@ struct tty_bufhead { struct tty_buffer *head; /* Queue head */ struct tty_buffer *tail; /* Active buffer */ struct tty_buffer *free; /* Free queue head */ - int memory_used; /* Buffer space used excluding free queue */ + int memory_used; /* Buffer space used excluding + free queue */ }; /* * When a break, frame error, or parity error happens, these codes are @@ -101,68 +102,68 @@ struct tty_bufhead { #define LNEXT_CHAR(tty) ((tty)->termios->c_cc[VLNEXT]) #define EOL2_CHAR(tty) ((tty)->termios->c_cc[VEOL2]) -#define _I_FLAG(tty,f) ((tty)->termios->c_iflag & (f)) -#define _O_FLAG(tty,f) ((tty)->termios->c_oflag & (f)) -#define _C_FLAG(tty,f) ((tty)->termios->c_cflag & (f)) -#define _L_FLAG(tty,f) ((tty)->termios->c_lflag & (f)) - -#define I_IGNBRK(tty) _I_FLAG((tty),IGNBRK) -#define I_BRKINT(tty) _I_FLAG((tty),BRKINT) -#define I_IGNPAR(tty) _I_FLAG((tty),IGNPAR) -#define I_PARMRK(tty) _I_FLAG((tty),PARMRK) -#define I_INPCK(tty) _I_FLAG((tty),INPCK) -#define I_ISTRIP(tty) _I_FLAG((tty),ISTRIP) -#define I_INLCR(tty) _I_FLAG((tty),INLCR) -#define I_IGNCR(tty) _I_FLAG((tty),IGNCR) -#define I_ICRNL(tty) _I_FLAG((tty),ICRNL) -#define I_IUCLC(tty) _I_FLAG((tty),IUCLC) -#define I_IXON(tty) _I_FLAG((tty),IXON) -#define I_IXANY(tty) _I_FLAG((tty),IXANY) -#define I_IXOFF(tty) _I_FLAG((tty),IXOFF) -#define I_IMAXBEL(tty) _I_FLAG((tty),IMAXBEL) -#define I_IUTF8(tty) _I_FLAG((tty),IUTF8) - -#define O_OPOST(tty) _O_FLAG((tty),OPOST) -#define O_OLCUC(tty) _O_FLAG((tty),OLCUC) -#define O_ONLCR(tty) _O_FLAG((tty),ONLCR) -#define O_OCRNL(tty) _O_FLAG((tty),OCRNL) -#define O_ONOCR(tty) _O_FLAG((tty),ONOCR) -#define O_ONLRET(tty) _O_FLAG((tty),ONLRET) -#define O_OFILL(tty) _O_FLAG((tty),OFILL) -#define O_OFDEL(tty) _O_FLAG((tty),OFDEL) -#define O_NLDLY(tty) _O_FLAG((tty),NLDLY) -#define O_CRDLY(tty) _O_FLAG((tty),CRDLY) -#define O_TABDLY(tty) _O_FLAG((tty),TABDLY) -#define O_BSDLY(tty) _O_FLAG((tty),BSDLY) -#define O_VTDLY(tty) _O_FLAG((tty),VTDLY) -#define O_FFDLY(tty) _O_FLAG((tty),FFDLY) - -#define C_BAUD(tty) _C_FLAG((tty),CBAUD) -#define C_CSIZE(tty) _C_FLAG((tty),CSIZE) -#define C_CSTOPB(tty) _C_FLAG((tty),CSTOPB) -#define C_CREAD(tty) _C_FLAG((tty),CREAD) -#define C_PARENB(tty) _C_FLAG((tty),PARENB) -#define C_PARODD(tty) _C_FLAG((tty),PARODD) -#define C_HUPCL(tty) _C_FLAG((tty),HUPCL) -#define C_CLOCAL(tty) _C_FLAG((tty),CLOCAL) -#define C_CIBAUD(tty) _C_FLAG((tty),CIBAUD) -#define C_CRTSCTS(tty) _C_FLAG((tty),CRTSCTS) - -#define L_ISIG(tty) _L_FLAG((tty),ISIG) -#define L_ICANON(tty) _L_FLAG((tty),ICANON) -#define L_XCASE(tty) _L_FLAG((tty),XCASE) -#define L_ECHO(tty) _L_FLAG((tty),ECHO) -#define L_ECHOE(tty) _L_FLAG((tty),ECHOE) -#define L_ECHOK(tty) _L_FLAG((tty),ECHOK) -#define L_ECHONL(tty) _L_FLAG((tty),ECHONL) -#define L_NOFLSH(tty) _L_FLAG((tty),NOFLSH) -#define L_TOSTOP(tty) _L_FLAG((tty),TOSTOP) -#define L_ECHOCTL(tty) _L_FLAG((tty),ECHOCTL) -#define L_ECHOPRT(tty) _L_FLAG((tty),ECHOPRT) -#define L_ECHOKE(tty) _L_FLAG((tty),ECHOKE) -#define L_FLUSHO(tty) _L_FLAG((tty),FLUSHO) -#define L_PENDIN(tty) _L_FLAG((tty),PENDIN) -#define L_IEXTEN(tty) _L_FLAG((tty),IEXTEN) +#define _I_FLAG(tty, f) ((tty)->termios->c_iflag & (f)) +#define _O_FLAG(tty, f) ((tty)->termios->c_oflag & (f)) +#define _C_FLAG(tty, f) ((tty)->termios->c_cflag & (f)) +#define _L_FLAG(tty, f) ((tty)->termios->c_lflag & (f)) + +#define I_IGNBRK(tty) _I_FLAG((tty), IGNBRK) +#define I_BRKINT(tty) _I_FLAG((tty), BRKINT) +#define I_IGNPAR(tty) _I_FLAG((tty), IGNPAR) +#define I_PARMRK(tty) _I_FLAG((tty), PARMRK) +#define I_INPCK(tty) _I_FLAG((tty), INPCK) +#define I_ISTRIP(tty) _I_FLAG((tty), ISTRIP) +#define I_INLCR(tty) _I_FLAG((tty), INLCR) +#define I_IGNCR(tty) _I_FLAG((tty), IGNCR) +#define I_ICRNL(tty) _I_FLAG((tty), ICRNL) +#define I_IUCLC(tty) _I_FLAG((tty), IUCLC) +#define I_IXON(tty) _I_FLAG((tty), IXON) +#define I_IXANY(tty) _I_FLAG((tty), IXANY) +#define I_IXOFF(tty) _I_FLAG((tty), IXOFF) +#define I_IMAXBEL(tty) _I_FLAG((tty), IMAXBEL) +#define I_IUTF8(tty) _I_FLAG((tty), IUTF8) + +#define O_OPOST(tty) _O_FLAG((tty), OPOST) +#define O_OLCUC(tty) _O_FLAG((tty), OLCUC) +#define O_ONLCR(tty) _O_FLAG((tty), ONLCR) +#define O_OCRNL(tty) _O_FLAG((tty), OCRNL) +#define O_ONOCR(tty) _O_FLAG((tty), ONOCR) +#define O_ONLRET(tty) _O_FLAG((tty), ONLRET) +#define O_OFILL(tty) _O_FLAG((tty), OFILL) +#define O_OFDEL(tty) _O_FLAG((tty), OFDEL) +#define O_NLDLY(tty) _O_FLAG((tty), NLDLY) +#define O_CRDLY(tty) _O_FLAG((tty), CRDLY) +#define O_TABDLY(tty) _O_FLAG((tty), TABDLY) +#define O_BSDLY(tty) _O_FLAG((tty), BSDLY) +#define O_VTDLY(tty) _O_FLAG((tty), VTDLY) +#define O_FFDLY(tty) _O_FLAG((tty), FFDLY) + +#define C_BAUD(tty) _C_FLAG((tty), CBAUD) +#define C_CSIZE(tty) _C_FLAG((tty), CSIZE) +#define C_CSTOPB(tty) _C_FLAG((tty), CSTOPB) +#define C_CREAD(tty) _C_FLAG((tty), CREAD) +#define C_PARENB(tty) _C_FLAG((tty), PARENB) +#define C_PARODD(tty) _C_FLAG((tty), PARODD) +#define C_HUPCL(tty) _C_FLAG((tty), HUPCL) +#define C_CLOCAL(tty) _C_FLAG((tty), CLOCAL) +#define C_CIBAUD(tty) _C_FLAG((tty), CIBAUD) +#define C_CRTSCTS(tty) _C_FLAG((tty), CRTSCTS) + +#define L_ISIG(tty) _L_FLAG((tty), ISIG) +#define L_ICANON(tty) _L_FLAG((tty), ICANON) +#define L_XCASE(tty) _L_FLAG((tty), XCASE) +#define L_ECHO(tty) _L_FLAG((tty), ECHO) +#define L_ECHOE(tty) _L_FLAG((tty), ECHOE) +#define L_ECHOK(tty) _L_FLAG((tty), ECHOK) +#define L_ECHONL(tty) _L_FLAG((tty), ECHONL) +#define L_NOFLSH(tty) _L_FLAG((tty), NOFLSH) +#define L_TOSTOP(tty) _L_FLAG((tty), TOSTOP) +#define L_ECHOCTL(tty) _L_FLAG((tty), ECHOCTL) +#define L_ECHOPRT(tty) _L_FLAG((tty), ECHOPRT) +#define L_ECHOKE(tty) _L_FLAG((tty), ECHOKE) +#define L_FLUSHO(tty) _L_FLAG((tty), FLUSHO) +#define L_PENDIN(tty) _L_FLAG((tty), PENDIN) +#define L_IEXTEN(tty) _L_FLAG((tty), IEXTEN) struct device; struct signal_struct; @@ -177,7 +178,7 @@ struct signal_struct; * of the tty object but in many cases port -> tty mappings are valid only * until a hangup so don't use the wrong path. */ - + struct tty_port { struct tty_struct *tty; /* Back pointer */ int blocked_open; /* Waiting to open */ @@ -273,14 +274,14 @@ struct tty_struct { /* * These bits are used in the flags field of the tty structure. - * + * * So that interrupts won't be able to mess up the queues, * copy_to_cooked must be atomic with respect to itself, as must * tty->write. Thus, you must use the inline functions set_bit() and * clear_bit() to make things atomic. */ #define TTY_THROTTLED 0 /* Call unthrottle() at threshold min */ -#define TTY_IO_ERROR 1 /* Canse an I/O error (may be no ldisc too) */ +#define TTY_IO_ERROR 1 /* Cause an I/O error (may be no ldisc too) */ #define TTY_OTHER_CLOSED 2 /* Other side (if any) has closed */ #define TTY_EXCLUSIVE 3 /* Exclusive open mode */ #define TTY_DEBUG 4 /* Debugging */ @@ -310,10 +311,10 @@ extern int vcs_init(void); extern int tty_paranoia_check(struct tty_struct *tty, struct inode *inode, const char *routine); extern char *tty_name(struct tty_struct *tty, char *buf); -extern void tty_wait_until_sent(struct tty_struct * tty, long timeout); -extern int tty_check_change(struct tty_struct * tty); -extern void stop_tty(struct tty_struct * tty); -extern void start_tty(struct tty_struct * tty); +extern void tty_wait_until_sent(struct tty_struct *tty, long timeout); +extern int tty_check_change(struct tty_struct *tty); +extern void stop_tty(struct tty_struct *tty); +extern void start_tty(struct tty_struct *tty); extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc); extern int tty_unregister_ldisc(int disc); extern int tty_register_driver(struct tty_driver *driver); @@ -335,10 +336,10 @@ extern int is_current_pgrp_orphaned(void); extern struct pid *tty_get_pgrp(struct tty_struct *tty); extern int is_ignored(int sig); extern int tty_signal(int sig, struct tty_struct *tty); -extern void tty_hangup(struct tty_struct * tty); -extern void tty_vhangup(struct tty_struct * tty); +extern void tty_hangup(struct tty_struct *tty); +extern void tty_vhangup(struct tty_struct *tty); extern void tty_unhangup(struct file *filp); -extern int tty_hung_up_p(struct file * filp); +extern int tty_hung_up_p(struct file *filp); extern void do_SAK(struct tty_struct *tty); extern void __do_SAK(struct tty_struct *tty); extern void disassociate_ctty(int priv); @@ -347,8 +348,10 @@ extern void tty_flip_buffer_push(struct tty_struct *tty); extern speed_t tty_get_baud_rate(struct tty_struct *tty); extern speed_t tty_termios_baud_rate(struct ktermios *termios); extern speed_t tty_termios_input_baud_rate(struct ktermios *termios); -extern void tty_termios_encode_baud_rate(struct ktermios *termios, speed_t ibaud, speed_t obaud); -extern void tty_encode_baud_rate(struct tty_struct *tty, speed_t ibaud, speed_t obaud); +extern void tty_termios_encode_baud_rate(struct ktermios *termios, + speed_t ibaud, speed_t obaud); +extern void tty_encode_baud_rate(struct tty_struct *tty, + speed_t ibaud, speed_t obaud); extern void tty_termios_copy_hw(struct ktermios *new, struct ktermios *old); extern int tty_termios_hw_change(struct ktermios *a, struct ktermios *b); @@ -390,7 +393,8 @@ extern void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, extern void tty_audit_exit(void); extern void tty_audit_fork(struct signal_struct *sig); extern void tty_audit_push(struct tty_struct *tty); -extern void tty_audit_push_task(struct task_struct *tsk, uid_t loginuid, u32 sessionid); +extern void tty_audit_push_task(struct task_struct *tsk, + uid_t loginuid, u32 sessionid); #else static inline void tty_audit_add_data(struct tty_struct *tty, unsigned char *data, size_t size) @@ -405,19 +409,20 @@ static inline void tty_audit_fork(struct signal_struct *sig) static inline void tty_audit_push(struct tty_struct *tty) { } -static inline void tty_audit_push_task(struct task_struct *tsk, uid_t loginuid, u32 sessionid) +static inline void tty_audit_push_task(struct task_struct *tsk, + uid_t loginuid, u32 sessionid) { } #endif /* tty_ioctl.c */ -extern int n_tty_ioctl(struct tty_struct * tty, struct file * file, +extern int n_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); /* serial.c */ extern void serial_console_init(void); - + /* pcxx.c */ extern int pcxe_open(struct tty_struct *tty, struct file *filp); @@ -428,7 +433,7 @@ extern void console_print(const char *); /* vt.c */ -extern int vt_ioctl(struct tty_struct *tty, struct file * file, +extern int vt_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); #endif /* __KERNEL__ */ -- cgit v1.2.3 From 4982d6b37a5ccebe6c2af79970c7a15c1939243a Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 16 Jul 2008 21:55:11 +0100 Subject: esp: use tty_port Switch esp to use the new tty_port structures Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/esp.c | 272 +++++++++++++++++++++++------------------------ include/linux/hayesesp.h | 9 +- 2 files changed, 138 insertions(+), 143 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/esp.c b/drivers/char/esp.c index 84840ba13ff0..2eaf09f93e3d 100644 --- a/drivers/char/esp.c +++ b/drivers/char/esp.c @@ -128,9 +128,9 @@ static struct tty_driver *esp_driver; #if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT) #define DBG_CNT(s) printk(KERN_DEBUG "(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \ - tty->name, info->flags, \ + tty->name, info->port.flags, \ serial_driver.refcount, \ - info->count, tty->count, s) + info->port.count, tty->count, s) #else #define DBG_CNT(s) #endif @@ -172,13 +172,13 @@ static inline int serial_paranoia_check(struct esp_struct *info, static inline unsigned int serial_in(struct esp_struct *info, int offset) { - return inb(info->port + offset); + return inb(info->io_port + offset); } static inline void serial_out(struct esp_struct *info, int offset, unsigned char value) { - outb(value, info->port+offset); + outb(value, info->io_port+offset); } /* @@ -273,7 +273,7 @@ static inline void release_pio_buffer(struct esp_pio_buffer *buf) static inline void receive_chars_pio(struct esp_struct *info, int num_bytes) { - struct tty_struct *tty = info->tty; + struct tty_struct *tty = info->port.tty; int i; struct esp_pio_buffer *pio_buf; struct esp_pio_buffer *err_buf; @@ -295,7 +295,7 @@ static inline void receive_chars_pio(struct esp_struct *info, int num_bytes) for (i = 0; i < num_bytes - 1; i += 2) { *((unsigned short *)(pio_buf->data + i)) = - inw(info->port + UART_ESI_RX); + inw(info->io_port + UART_ESI_RX); err_buf->data[i] = serial_in(info, UART_ESI_RWS); err_buf->data[i + 1] = (err_buf->data[i] >> 3) & status_mask; err_buf->data[i] &= status_mask; @@ -308,7 +308,7 @@ static inline void receive_chars_pio(struct esp_struct *info, int num_bytes) } /* make sure everything is still ok since interrupts were enabled */ - tty = info->tty; + tty = info->port.tty; if (!tty) { release_pio_buffer(pio_buf); @@ -325,7 +325,7 @@ static inline void receive_chars_pio(struct esp_struct *info, int num_bytes) if (err_buf->data[i] & 0x04) { flag = TTY_BREAK; - if (info->flags & ASYNC_SAK) + if (info->port.flags & ASYNC_SAK) do_SAK(tty); } else if (err_buf->data[i] & 0x02) flag = TTY_FRAME; @@ -370,7 +370,7 @@ static void receive_chars_dma(struct esp_struct *info, int num_bytes) static inline void receive_chars_dma_done(struct esp_struct *info, int status) { - struct tty_struct *tty = info->tty; + struct tty_struct *tty = info->port.tty; int num_bytes; unsigned long flags; @@ -396,7 +396,7 @@ static inline void receive_chars_dma_done(struct esp_struct *info, if (status & 0x10) { statflag = TTY_BREAK; (info->icount.brk)++; - if (info->flags & ASYNC_SAK) + if (info->port.flags & ASYNC_SAK) do_SAK(tty); } else if (status & 0x08) { statflag = TTY_FRAME; @@ -451,7 +451,7 @@ static inline void transmit_chars_pio(struct esp_struct *info, for (i = 0; i < space_avail - 1; i += 2) { outw(*((unsigned short *)(pio_buf->data + i)), - info->port + UART_ESI_TX); + info->io_port + UART_ESI_TX); } if (space_avail & 0x0001) @@ -470,8 +470,8 @@ static inline void transmit_chars_pio(struct esp_struct *info, } if (info->xmit_cnt < WAKEUP_CHARS) { - if (info->tty) - tty_wakeup(info->tty); + if (info->port.tty) + tty_wakeup(info->port.tty); #ifdef SERIAL_DEBUG_INTR printk("THRE..."); @@ -507,8 +507,8 @@ static inline void transmit_chars_dma(struct esp_struct *info, int num_bytes) info->xmit_tail = (info->xmit_tail + dma_bytes) & (ESP_XMIT_SIZE - 1); if (info->xmit_cnt < WAKEUP_CHARS) { - if (info->tty) - tty_wakeup(info->tty); + if (info->port.tty) + tty_wakeup(info->port.tty); #ifdef SERIAL_DEBUG_INTR printk("THRE..."); @@ -575,18 +575,18 @@ static void check_modem_status(struct esp_struct *info) wake_up_interruptible(&info->delta_msr_wait); } - if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) { + if ((info->port.flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) { #if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR)) printk("ttys%d CD now %s...", info->line, (status & UART_MSR_DCD) ? "on" : "off"); #endif if (status & UART_MSR_DCD) - wake_up_interruptible(&info->open_wait); + wake_up_interruptible(&info->port.open_wait); else { #ifdef SERIAL_DEBUG_OPEN printk("scheduling hangup..."); #endif - tty_hangup(info->tty); + tty_hangup(info->port.tty); } } } @@ -609,7 +609,7 @@ static irqreturn_t rs_interrupt_single(int irq, void *dev_id) spin_lock(&info->lock); - if (!info->tty) { + if (!info->port.tty) { spin_unlock(&info->lock); return IRQ_NONE; } @@ -647,7 +647,7 @@ static irqreturn_t rs_interrupt_single(int irq, void *dev_id) num_bytes = serial_in(info, UART_ESI_STAT1) << 8; num_bytes |= serial_in(info, UART_ESI_STAT2); - num_bytes = tty_buffer_request_room(info->tty, num_bytes); + num_bytes = tty_buffer_request_room(info->port.tty, num_bytes); if (num_bytes) { if (dma_bytes || @@ -661,7 +661,7 @@ static irqreturn_t rs_interrupt_single(int irq, void *dev_id) if (!(info->stat_flags & (ESP_STAT_DMA_RX | ESP_STAT_DMA_TX)) && (scratch & 0x02) && (info->IER & UART_IER_THRI)) { - if ((info->xmit_cnt <= 0) || info->tty->stopped) { + if ((info->xmit_cnt <= 0) || info->port.tty->stopped) { info->IER &= ~UART_IER_THRI; serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); serial_out(info, UART_ESI_CMD2, info->IER); @@ -782,7 +782,7 @@ static int startup(struct esp_struct *info) spin_lock_irqsave(&info->lock, flags); - if (info->flags & ASYNC_INITIALIZED) + if (info->port.flags & ASYNC_INITIALIZED) goto out; if (!info->xmit_buf) { @@ -806,7 +806,7 @@ static int startup(struct esp_struct *info) num_chars |= serial_in(info, UART_ESI_STAT2); while (num_chars > 1) { - inw(info->port + UART_ESI_RX); + inw(info->io_port + UART_ESI_RX); num_chars -= 2; } @@ -834,9 +834,9 @@ static int startup(struct esp_struct *info) if (retval) { if (capable(CAP_SYS_ADMIN)) { - if (info->tty) + if (info->port.tty) set_bit(TTY_IO_ERROR, - &info->tty->flags); + &info->port.tty->flags); retval = 0; } goto out_unlocked; @@ -874,30 +874,30 @@ static int startup(struct esp_struct *info) serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); serial_out(info, UART_ESI_CMD2, info->IER); - if (info->tty) - clear_bit(TTY_IO_ERROR, &info->tty->flags); + if (info->port.tty) + clear_bit(TTY_IO_ERROR, &info->port.tty->flags); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; spin_unlock_irqrestore(&info->lock, flags); /* * Set up the tty->alt_speed kludge */ - if (info->tty) { - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - info->tty->alt_speed = 57600; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - info->tty->alt_speed = 115200; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - info->tty->alt_speed = 230400; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - info->tty->alt_speed = 460800; + if (info->port.tty) { + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->port.tty->alt_speed = 57600; + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->port.tty->alt_speed = 115200; + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->port.tty->alt_speed = 230400; + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->port.tty->alt_speed = 460800; } /* * set the speed of the serial port */ change_speed(info); - info->flags |= ASYNC_INITIALIZED; + info->port.flags |= ASYNC_INITIALIZED; return 0; out: @@ -914,7 +914,7 @@ static void shutdown(struct esp_struct *info) { unsigned long flags, f; - if (!(info->flags & ASYNC_INITIALIZED)) + if (!(info->port.flags & ASYNC_INITIALIZED)) return; #ifdef SERIAL_DEBUG_OPEN @@ -951,7 +951,7 @@ static void shutdown(struct esp_struct *info) while (current_port) { if ((current_port != info) && - (current_port->flags & ASYNC_INITIALIZED)) + (current_port->port.flags & ASYNC_INITIALIZED)) break; current_port = current_port->next_port; @@ -974,7 +974,7 @@ static void shutdown(struct esp_struct *info) serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); serial_out(info, UART_ESI_CMD2, 0x00); - if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) + if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL)) info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); info->MCR &= ~UART_MCR_OUT2; @@ -982,10 +982,10 @@ static void shutdown(struct esp_struct *info) serial_out(info, UART_ESI_CMD2, UART_MCR); serial_out(info, UART_ESI_CMD2, info->MCR); - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); + if (info->port.tty) + set_bit(TTY_IO_ERROR, &info->port.tty->flags); - info->flags &= ~ASYNC_INITIALIZED; + info->port.flags &= ~ASYNC_INITIALIZED; spin_unlock_irqrestore(&info->lock, flags); } @@ -1002,10 +1002,10 @@ static void change_speed(struct esp_struct *info) unsigned char flow1 = 0, flow2 = 0; unsigned long flags; - if (!info->tty || !info->tty->termios) + if (!info->port.tty || !info->port.tty->termios) return; - cflag = info->tty->termios->c_cflag; - port = info->port; + cflag = info->port.tty->termios->c_cflag; + port = info->io_port; /* byte size and parity */ switch (cflag & CSIZE) { @@ -1029,9 +1029,9 @@ static void change_speed(struct esp_struct *info) if (cflag & CMSPAR) cval |= UART_LCR_SPAR; #endif - baud = tty_get_baud_rate(info->tty); + baud = tty_get_baud_rate(info->port.tty); if (baud == 38400 && - ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) quot = info->custom_divisor; else { if (baud == 134) /* Special case since 134 is really 134.5 */ @@ -1046,49 +1046,49 @@ static void change_speed(struct esp_struct *info) if (baud) { /* Actual rate */ baud = BASE_BAUD/quot; - tty_encode_baud_rate(info->tty, baud, baud); + tty_encode_baud_rate(info->port.tty, baud, baud); } info->timeout = ((1024 * HZ * bits * quot) / BASE_BAUD) + (HZ / 50); /* CTS flow control flag and modem status interrupts */ /* info->IER &= ~UART_IER_MSI; */ if (cflag & CRTSCTS) { - info->flags |= ASYNC_CTS_FLOW; + info->port.flags |= ASYNC_CTS_FLOW; /* info->IER |= UART_IER_MSI; */ flow1 = 0x04; flow2 = 0x10; } else - info->flags &= ~ASYNC_CTS_FLOW; + info->port.flags &= ~ASYNC_CTS_FLOW; if (cflag & CLOCAL) - info->flags &= ~ASYNC_CHECK_CD; + info->port.flags &= ~ASYNC_CHECK_CD; else - info->flags |= ASYNC_CHECK_CD; + info->port.flags |= ASYNC_CHECK_CD; /* * Set up parity check flag */ info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; - if (I_INPCK(info->tty)) + if (I_INPCK(info->port.tty)) info->read_status_mask |= UART_LSR_FE | UART_LSR_PE; - if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) + if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty)) info->read_status_mask |= UART_LSR_BI; info->ignore_status_mask = 0; #if 0 /* This should be safe, but for some broken bits of hardware... */ - if (I_IGNPAR(info->tty)) { + if (I_IGNPAR(info->port.tty)) { info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; info->read_status_mask |= UART_LSR_PE | UART_LSR_FE; } #endif - if (I_IGNBRK(info->tty)) { + if (I_IGNBRK(info->port.tty)) { info->ignore_status_mask |= UART_LSR_BI; info->read_status_mask |= UART_LSR_BI; /* * If we're ignore parity and break indicators, ignore * overruns too. (For real raw support). */ - if (I_IGNPAR(info->tty)) { + if (I_IGNPAR(info->port.tty)) { info->ignore_status_mask |= UART_LSR_OE | \ UART_LSR_PE | UART_LSR_FE; info->read_status_mask |= UART_LSR_OE | \ @@ -1096,7 +1096,7 @@ static void change_speed(struct esp_struct *info) } } - if (I_IXOFF(info->tty)) + if (I_IXOFF(info->port.tty)) flow1 |= 0x81; spin_lock_irqsave(&info->lock, flags); @@ -1116,10 +1116,10 @@ static void change_speed(struct esp_struct *info) serial_out(info, UART_ESI_CMD2, flow2); /* set flow control characters (XON/XOFF only) */ - if (I_IXOFF(info->tty)) { + if (I_IXOFF(info->port.tty)) { serial_out(info, UART_ESI_CMD1, ESI_SET_FLOW_CHARS); - serial_out(info, UART_ESI_CMD2, START_CHAR(info->tty)); - serial_out(info, UART_ESI_CMD2, STOP_CHAR(info->tty)); + serial_out(info, UART_ESI_CMD2, START_CHAR(info->port.tty)); + serial_out(info, UART_ESI_CMD2, STOP_CHAR(info->port.tty)); serial_out(info, UART_ESI_CMD2, 0x10); serial_out(info, UART_ESI_CMD2, 0x21); switch (cflag & CSIZE) { @@ -1355,9 +1355,9 @@ static int get_serial_info(struct esp_struct *info, memset(&tmp, 0, sizeof(tmp)); tmp.type = PORT_16550A; tmp.line = info->line; - tmp.port = info->port; + tmp.port = info->io_port; tmp.irq = info->irq; - tmp.flags = info->flags; + tmp.flags = info->port.flags; tmp.xmit_fifo_size = 1024; tmp.baud_base = BASE_BAUD; tmp.close_delay = info->close_delay; @@ -1407,7 +1407,7 @@ static int set_serial_info(struct esp_struct *info, if ((new_serial.type != PORT_16550A) || (new_serial.hub6) || - (info->port != new_serial.port) || + (info->io_port != new_serial.port) || (new_serial.baud_base != BASE_BAUD) || (new_serial.irq > 15) || (new_serial.irq < 2) || @@ -1425,9 +1425,9 @@ static int set_serial_info(struct esp_struct *info, if (change_irq || (new_serial.close_delay != info->close_delay) || ((new_serial.flags & ~ASYNC_USR_MASK) != - (info->flags & ~ASYNC_USR_MASK))) + (info->port.flags & ~ASYNC_USR_MASK))) return -EPERM; - info->flags = ((info->flags & ~ASYNC_USR_MASK) | + info->port.flags = ((info->port.flags & ~ASYNC_USR_MASK) | (new_serial.flags & ASYNC_USR_MASK)); info->custom_divisor = new_serial.custom_divisor; } else { @@ -1441,9 +1441,9 @@ static int set_serial_info(struct esp_struct *info, if ((current_async->line >= info->line) && (current_async->line < (info->line + 8))) { if (current_async == info) { - if (current_async->count > 1) + if (current_async->port.count > 1) return -EBUSY; - } else if (current_async->count) + } else if (current_async->port.count) return -EBUSY; } @@ -1456,7 +1456,7 @@ static int set_serial_info(struct esp_struct *info, * At this point, we start making changes..... */ - info->flags = ((info->flags & ~ASYNC_FLAGS) | + info->port.flags = ((info->port.flags & ~ASYNC_FLAGS) | (new_serial.flags & ASYNC_FLAGS)); info->custom_divisor = new_serial.custom_divisor; info->close_delay = new_serial.close_delay * HZ/100; @@ -1487,18 +1487,18 @@ static int set_serial_info(struct esp_struct *info, } } - if (info->flags & ASYNC_INITIALIZED) { - if (((old_info.flags & ASYNC_SPD_MASK) != - (info->flags & ASYNC_SPD_MASK)) || + if (info->port.flags & ASYNC_INITIALIZED) { + if (((old_info.port.flags & ASYNC_SPD_MASK) != + (info->port.flags & ASYNC_SPD_MASK)) || (old_info.custom_divisor != info->custom_divisor)) { - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - info->tty->alt_speed = 57600; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - info->tty->alt_speed = 115200; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - info->tty->alt_speed = 230400; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - info->tty->alt_speed = 460800; + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->port.tty->alt_speed = 57600; + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->port.tty->alt_speed = 115200; + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->port.tty->alt_speed = 230400; + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->port.tty->alt_speed = 460800; change_speed(info); } } else @@ -1554,9 +1554,9 @@ static int set_esp_config(struct esp_struct *info, while (current_async) { if (current_async == info) { - if (current_async->count > 1) + if (current_async->port.count > 1) return -EBUSY; - } else if (current_async->count) + } else if (current_async->port.count) return -EBUSY; current_async = current_async->next_port; @@ -1578,7 +1578,7 @@ static int set_esp_config(struct esp_struct *info, spin_unlock_irqrestore(&info->lock, flags); } else { /* DMA mode to PIO mode only */ - if (info->count > 1) + if (info->port.count > 1) return -EBUSY; shutdown(info); @@ -1634,7 +1634,7 @@ static int set_esp_config(struct esp_struct *info, spin_unlock_irqrestore(&info->lock, flags); } - if (!(info->flags & ASYNC_INITIALIZED)) + if (!(info->port.flags & ASYNC_INITIALIZED)) retval = startup(info); return retval; @@ -1917,9 +1917,9 @@ static void rs_close(struct tty_struct *tty, struct file *filp) #ifdef SERIAL_DEBUG_OPEN printk(KERN_DEBUG "rs_close ttys%d, count = %d\n", - info->line, info->count); + info->line, info->port.count); #endif - if (tty->count == 1 && info->count != 1) { + if (tty->count == 1 && info->port.count != 1) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->count should always @@ -1927,19 +1927,19 @@ static void rs_close(struct tty_struct *tty, struct file *filp) * one, we've got real problems, since it means the * serial port won't be shutdown. */ - printk(KERN_DEBUG "rs_close: bad serial port count; tty->count is 1, info->count is %d\n", info->count); - info->count = 1; + printk(KERN_DEBUG "rs_close: bad serial port count; tty->count is 1, info->port.count is %d\n", info->port.count); + info->port.count = 1; } - if (--info->count < 0) { + if (--info->port.count < 0) { printk(KERN_ERR "rs_close: bad serial port count for ttys%d: %d\n", - info->line, info->count); - info->count = 0; + info->line, info->port.count); + info->port.count = 0; } - if (info->count) { + if (info->port.count) { DBG_CNT("before DEC-2"); goto out; } - info->flags |= ASYNC_CLOSING; + info->port.flags |= ASYNC_CLOSING; spin_unlock_irqrestore(&info->lock, flags); /* @@ -1958,7 +1958,7 @@ static void rs_close(struct tty_struct *tty, struct file *filp) /* info->IER &= ~UART_IER_RLSI; */ info->IER &= ~UART_IER_RDI; info->read_status_mask &= ~UART_LSR_DR; - if (info->flags & ASYNC_INITIALIZED) { + if (info->port.flags & ASYNC_INITIALIZED) { spin_lock_irqsave(&info->lock, flags); serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); @@ -1981,15 +1981,15 @@ static void rs_close(struct tty_struct *tty, struct file *filp) rs_flush_buffer(tty); tty_ldisc_flush(tty); tty->closing = 0; - info->tty = NULL; + info->port.tty = NULL; - if (info->blocked_open) { + if (info->port.blocked_open) { if (info->close_delay) msleep_interruptible(jiffies_to_msecs(info->close_delay)); - wake_up_interruptible(&info->open_wait); + wake_up_interruptible(&info->port.open_wait); } - info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&info->close_wait); + info->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); + wake_up_interruptible(&info->port.close_wait); return; out: @@ -2047,10 +2047,10 @@ static void esp_hangup(struct tty_struct *tty) rs_flush_buffer(tty); shutdown(info); - info->count = 0; - info->flags &= ~ASYNC_NORMAL_ACTIVE; - info->tty = NULL; - wake_up_interruptible(&info->open_wait); + info->port.count = 0; + info->port.flags &= ~ASYNC_NORMAL_ACTIVE; + info->port.tty = NULL; + wake_up_interruptible(&info->port.open_wait); } /* @@ -2071,11 +2071,11 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, * until it's done, and then try again. */ if (tty_hung_up_p(filp) || - (info->flags & ASYNC_CLOSING)) { - if (info->flags & ASYNC_CLOSING) - interruptible_sleep_on(&info->close_wait); + (info->port.flags & ASYNC_CLOSING)) { + if (info->port.flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->port.close_wait); #ifdef SERIAL_DO_RESTART - if (info->flags & ASYNC_HUP_NOTIFY) + if (info->port.flags & ASYNC_HUP_NOTIFY) return -EAGAIN; else return -ERESTARTSYS; @@ -2090,7 +2090,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, */ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { - info->flags |= ASYNC_NORMAL_ACTIVE; + info->port.flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -2100,20 +2100,20 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, /* * Block waiting for the carrier detect and the line to become * free (i.e., not in use by the callout). While we are in - * this loop, info->count is dropped by one, so that + * this loop, info->port.count is dropped by one, so that * rs_close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ retval = 0; - add_wait_queue(&info->open_wait, &wait); + add_wait_queue(&info->port.open_wait, &wait); #ifdef SERIAL_DEBUG_OPEN printk(KERN_DEBUG "block_til_ready before block: ttys%d, count = %d\n", - info->line, info->count); + info->line, info->port.count); #endif spin_lock_irqsave(&info->lock, flags); if (!tty_hung_up_p(filp)) - info->count--; - info->blocked_open++; + info->port.count--; + info->port.blocked_open++; while (1) { if ((tty->termios->c_cflag & CBAUD)) { unsigned int scratch; @@ -2128,9 +2128,9 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, } set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || - !(info->flags & ASYNC_INITIALIZED)) { + !(info->port.flags & ASYNC_INITIALIZED)) { #ifdef SERIAL_DO_RESTART - if (info->flags & ASYNC_HUP_NOTIFY) + if (info->port.flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; @@ -2144,7 +2144,7 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, if (serial_in(info, UART_ESI_STAT2) & UART_MSR_DCD) do_clocal = 1; - if (!(info->flags & ASYNC_CLOSING) && + if (!(info->port.flags & ASYNC_CLOSING) && (do_clocal)) break; if (signal_pending(current)) { @@ -2153,25 +2153,25 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, } #ifdef SERIAL_DEBUG_OPEN printk(KERN_DEBUG "block_til_ready blocking: ttys%d, count = %d\n", - info->line, info->count); + info->line, info->port.count); #endif spin_unlock_irqrestore(&info->lock, flags); schedule(); spin_lock_irqsave(&info->lock, flags); } set_current_state(TASK_RUNNING); - remove_wait_queue(&info->open_wait, &wait); + remove_wait_queue(&info->port.open_wait, &wait); if (!tty_hung_up_p(filp)) - info->count++; - info->blocked_open--; + info->port.count++; + info->port.blocked_open--; spin_unlock_irqrestore(&info->lock, flags); #ifdef SERIAL_DEBUG_OPEN printk(KERN_DEBUG "block_til_ready after blocking: ttys%d, count = %d\n", - info->line, info->count); + info->line, info->port.count); #endif if (retval) return retval; - info->flags |= ASYNC_NORMAL_ACTIVE; + info->port.flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -2204,12 +2204,12 @@ static int esp_open(struct tty_struct *tty, struct file *filp) } #ifdef SERIAL_DEBUG_OPEN - printk(KERN_DEBUG "esp_open %s, count = %d\n", tty->name, info->count); + printk(KERN_DEBUG "esp_open %s, count = %d\n", tty->name, info->port.count); #endif spin_lock_irqsave(&info->lock, flags); - info->count++; + info->port.count++; tty->driver_data = info; - info->tty = tty; + info->port.tty = tty; spin_unlock_irqrestore(&info->lock, flags); @@ -2263,7 +2263,7 @@ static int autoconfig(struct esp_struct *info) int port_detected = 0; unsigned long flags; - if (!request_region(info->port, REGION_SIZE, "esp serial")) + if (!request_region(info->io_port, REGION_SIZE, "esp serial")) return -EIO; spin_lock_irqsave(&info->lock, flags); @@ -2300,7 +2300,7 @@ static int autoconfig(struct esp_struct *info) } } if (!port_detected) - release_region(info->port, REGION_SIZE); + release_region(info->io_port, REGION_SIZE); spin_unlock_irqrestore(&info->lock, flags); return (port_detected); @@ -2414,7 +2414,7 @@ static int __init espserial_init(void) offset = 0; do { - info->port = esp[i] + offset; + info->io_port = esp[i] + offset; info->irq = irq[i]; info->line = (i * 8) + (offset / 8); @@ -2425,9 +2425,9 @@ static int __init espserial_init(void) } info->custom_divisor = (divisor[i] >> (offset / 2)) & 0xf; - info->flags = STD_COM_FLAGS; + info->port.flags = STD_COM_FLAGS; if (info->custom_divisor) - info->flags |= ASYNC_SPD_CUST; + info->port.flags |= ASYNC_SPD_CUST; info->magic = ESP_MAGIC; info->close_delay = 5*HZ/10; info->closing_wait = 30*HZ; @@ -2436,13 +2436,13 @@ static int __init espserial_init(void) info->config.flow_off = flow_off; info->config.pio_threshold = pio_threshold; info->next_port = ports; - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->close_wait); + init_waitqueue_head(&info->port.open_wait); + init_waitqueue_head(&info->port.close_wait); init_waitqueue_head(&info->delta_msr_wait); init_waitqueue_head(&info->break_wait); ports = info; printk(KERN_INFO "ttyP%d at 0x%04x (irq = %d) is an ESP ", - info->line, info->port, info->irq); + info->line, info->io_port, info->irq); if (info->line % 8) { printk("secondary port\n"); @@ -2498,8 +2498,8 @@ static void __exit espserial_exit(void) put_tty_driver(esp_driver); while (ports) { - if (ports->port) - release_region(ports->port, REGION_SIZE); + if (ports->io_port) + release_region(ports->io_port, REGION_SIZE); temp_async = ports->next_port; kfree(ports); ports = temp_async; diff --git a/include/linux/hayesesp.h b/include/linux/hayesesp.h index 2177ee5b2fe2..940aeb51d53f 100644 --- a/include/linux/hayesesp.h +++ b/include/linux/hayesesp.h @@ -76,11 +76,10 @@ struct hayes_esp_config { struct esp_struct { int magic; + struct tty_port port; spinlock_t lock; - int port; + int io_port; int irq; - int flags; /* defined in tty.h */ - struct tty_struct *tty; int read_status_mask; int ignore_status_mask; int timeout; @@ -93,14 +92,10 @@ struct esp_struct { int MCR; /* Modem control register */ unsigned long last_active; int line; - int count; /* # of fd on device */ - int blocked_open; /* # of blocked opens */ unsigned char *xmit_buf; int xmit_head; int xmit_tail; int xmit_cnt; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; wait_queue_head_t delta_msr_wait; wait_queue_head_t break_wait; struct async_icount icount; /* kernel counters for the 4 input interrupts */ -- cgit v1.2.3 From b5391e29f428d11755ca2c91074c6db6f5c69d7c Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 16 Jul 2008 21:55:20 +0100 Subject: gs: use tty_port Switch drivers using the old "generic serial" driver to use the tty_port structures Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/generic_serial.c | 158 ++++++++++++++++++++--------------------- drivers/char/rio/rio_linux.c | 20 +++--- drivers/char/rio/riocmd.c | 10 +-- drivers/char/rio/riointr.c | 10 +-- drivers/char/rio/rioparam.c | 2 +- drivers/char/rio/riotty.c | 16 ++--- drivers/char/sx.c | 115 +++++++++++++++--------------- include/linux/generic_serial.h | 8 +-- 8 files changed, 166 insertions(+), 173 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/generic_serial.c b/drivers/char/generic_serial.c index 252f73e48596..19d3afb0e50c 100644 --- a/drivers/char/generic_serial.c +++ b/drivers/char/generic_serial.c @@ -60,7 +60,7 @@ int gs_put_char(struct tty_struct * tty, unsigned char ch) if (!port) return 0; - if (! (port->flags & ASYNC_INITIALIZED)) return 0; + if (! (port->port.flags & ASYNC_INITIALIZED)) return 0; /* Take a lock on the serial tranmit buffer! */ mutex_lock(& port->port_write_mutex); @@ -103,7 +103,7 @@ int gs_write(struct tty_struct * tty, if (!port) return 0; - if (! (port->flags & ASYNC_INITIALIZED)) + if (! (port->port.flags & ASYNC_INITIALIZED)) return 0; /* get exclusive "write" access to this port (problem 3) */ @@ -141,13 +141,13 @@ int gs_write(struct tty_struct * tty, mutex_unlock(& port->port_write_mutex); gs_dprintk (GS_DEBUG_WRITE, "write: interrupts are %s\n", - (port->flags & GS_TX_INTEN)?"enabled": "disabled"); + (port->port.flags & GS_TX_INTEN)?"enabled": "disabled"); if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped && - !(port->flags & GS_TX_INTEN)) { - port->flags |= GS_TX_INTEN; + !(port->port.flags & GS_TX_INTEN)) { + port->port.flags |= GS_TX_INTEN; port->rd->enable_tx_interrupts (port); } func_exit (); @@ -208,7 +208,7 @@ static int gs_wait_tx_flushed (void * ptr, unsigned long timeout) gs_dprintk (GS_DEBUG_FLUSH, "port=%p.\n", port); if (port) { gs_dprintk (GS_DEBUG_FLUSH, "xmit_cnt=%x, xmit_buf=%p, tty=%p.\n", - port->xmit_cnt, port->xmit_buf, port->tty); + port->xmit_cnt, port->xmit_buf, port->port.tty); } if (!port || port->xmit_cnt < 0 || !port->xmit_buf) { @@ -217,7 +217,7 @@ static int gs_wait_tx_flushed (void * ptr, unsigned long timeout) return -EINVAL; /* This is an error which we don't know how to handle. */ } - rcib = gs_real_chars_in_buffer(port->tty); + rcib = gs_real_chars_in_buffer(port->port.tty); if(rcib <= 0) { gs_dprintk (GS_DEBUG_FLUSH, "nothing to wait for.\n"); @@ -236,7 +236,7 @@ static int gs_wait_tx_flushed (void * ptr, unsigned long timeout) /* the expression is actually jiffies < end_jiffies, but that won't work around the wraparound. Tricky eh? */ - while ((charsleft = gs_real_chars_in_buffer (port->tty)) && + while ((charsleft = gs_real_chars_in_buffer (port->port.tty)) && time_after (end_jiffies, jiffies)) { /* Units check: chars * (bits/char) * (jiffies /sec) / (bits/sec) = jiffies! @@ -309,7 +309,7 @@ void gs_flush_chars(struct tty_struct * tty) } /* Beats me -- REW */ - port->flags |= GS_TX_INTEN; + port->port.flags |= GS_TX_INTEN; port->rd->enable_tx_interrupts (port); func_exit (); } @@ -329,8 +329,8 @@ void gs_stop(struct tty_struct * tty) if (port->xmit_cnt && port->xmit_buf && - (port->flags & GS_TX_INTEN) ) { - port->flags &= ~GS_TX_INTEN; + (port->port.flags & GS_TX_INTEN) ) { + port->port.flags &= ~GS_TX_INTEN; port->rd->disable_tx_interrupts (port); } func_exit (); @@ -349,8 +349,8 @@ void gs_start(struct tty_struct * tty) if (port->xmit_cnt && port->xmit_buf && - !(port->flags & GS_TX_INTEN) ) { - port->flags |= GS_TX_INTEN; + !(port->port.flags & GS_TX_INTEN) ) { + port->port.flags |= GS_TX_INTEN; port->rd->enable_tx_interrupts (port); } func_exit (); @@ -365,7 +365,7 @@ static void gs_shutdown_port (struct gs_port *port) if (!port) return; - if (!(port->flags & ASYNC_INITIALIZED)) + if (!(port->port.flags & ASYNC_INITIALIZED)) return; spin_lock_irqsave(&port->driver_lock, flags); @@ -375,12 +375,12 @@ static void gs_shutdown_port (struct gs_port *port) port->xmit_buf = NULL; } - if (port->tty) - set_bit(TTY_IO_ERROR, &port->tty->flags); + if (port->port.tty) + set_bit(TTY_IO_ERROR, &port->port.tty->flags); port->rd->shutdown_port (port); - port->flags &= ~ASYNC_INITIALIZED; + port->port.flags &= ~ASYNC_INITIALIZED; spin_unlock_irqrestore(&port->driver_lock, flags); func_exit(); @@ -396,16 +396,16 @@ void gs_hangup(struct tty_struct *tty) if (!tty) return; port = tty->driver_data; - tty = port->tty; + tty = port->port.tty; if (!tty) return; gs_shutdown_port (port); - port->flags &= ~(ASYNC_NORMAL_ACTIVE|GS_ACTIVE); - port->tty = NULL; - port->count = 0; + port->port.flags &= ~(ASYNC_NORMAL_ACTIVE|GS_ACTIVE); + port->port.tty = NULL; + port->port.count = 0; - wake_up_interruptible(&port->open_wait); + wake_up_interruptible(&port->port.open_wait); func_exit (); } @@ -424,7 +424,7 @@ int gs_block_til_ready(void *port_, struct file * filp) if (!port) return 0; - tty = port->tty; + tty = port->port.tty; if (!tty) return 0; @@ -433,9 +433,9 @@ int gs_block_til_ready(void *port_, struct file * filp) * If the device is in the middle of being closed, then block * until it's done, and then try again. */ - if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { - interruptible_sleep_on(&port->close_wait); - if (port->flags & ASYNC_HUP_NOTIFY) + if (tty_hung_up_p(filp) || port->port.flags & ASYNC_CLOSING) { + interruptible_sleep_on(&port->port.close_wait); + if (port->port.flags & ASYNC_HUP_NOTIFY) return -EAGAIN; else return -ERESTARTSYS; @@ -449,7 +449,7 @@ int gs_block_til_ready(void *port_, struct file * filp) */ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { - port->flags |= ASYNC_NORMAL_ACTIVE; + port->port.flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -461,34 +461,34 @@ int gs_block_til_ready(void *port_, struct file * filp) /* * Block waiting for the carrier detect and the line to become * free (i.e., not in use by the callout). While we are in - * this loop, port->count is dropped by one, so that + * this loop, port->port.count is dropped by one, so that * rs_close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ retval = 0; - add_wait_queue(&port->open_wait, &wait); + add_wait_queue(&port->port.open_wait, &wait); gs_dprintk (GS_DEBUG_BTR, "after add waitq.\n"); spin_lock_irqsave(&port->driver_lock, flags); if (!tty_hung_up_p(filp)) { - port->count--; + port->port.count--; } spin_unlock_irqrestore(&port->driver_lock, flags); - port->blocked_open++; + port->port.blocked_open++; while (1) { CD = port->rd->get_CD (port); gs_dprintk (GS_DEBUG_BTR, "CD is now %d.\n", CD); set_current_state (TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || - !(port->flags & ASYNC_INITIALIZED)) { - if (port->flags & ASYNC_HUP_NOTIFY) + !(port->port.flags & ASYNC_INITIALIZED)) { + if (port->port.flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; break; } - if (!(port->flags & ASYNC_CLOSING) && + if (!(port->port.flags & ASYNC_CLOSING) && (do_clocal || CD)) break; gs_dprintk (GS_DEBUG_BTR, "signal_pending is now: %d (%lx)\n", @@ -500,17 +500,17 @@ int gs_block_til_ready(void *port_, struct file * filp) schedule(); } gs_dprintk (GS_DEBUG_BTR, "Got out of the loop. (%d)\n", - port->blocked_open); + port->port.blocked_open); set_current_state (TASK_RUNNING); - remove_wait_queue(&port->open_wait, &wait); + remove_wait_queue(&port->port.open_wait, &wait); if (!tty_hung_up_p(filp)) { - port->count++; + port->port.count++; } - port->blocked_open--; + port->port.blocked_open--; if (retval) return retval; - port->flags |= ASYNC_NORMAL_ACTIVE; + port->port.flags |= ASYNC_NORMAL_ACTIVE; func_exit (); return 0; } @@ -529,10 +529,10 @@ void gs_close(struct tty_struct * tty, struct file * filp) if (!port) return; - if (!port->tty) { + if (!port->port.tty) { /* This seems to happen when this is called from vhangup. */ - gs_dprintk (GS_DEBUG_CLOSE, "gs: Odd: port->tty is NULL\n"); - port->tty = tty; + gs_dprintk (GS_DEBUG_CLOSE, "gs: Odd: port->port.tty is NULL\n"); + port->port.tty = tty; } spin_lock_irqsave(&port->driver_lock, flags); @@ -545,23 +545,23 @@ void gs_close(struct tty_struct * tty, struct file * filp) return; } - if ((tty->count == 1) && (port->count != 1)) { + if ((tty->count == 1) && (port->port.count != 1)) { printk(KERN_ERR "gs: gs_close port %p: bad port count;" - " tty->count is 1, port count is %d\n", port, port->count); - port->count = 1; + " tty->count is 1, port count is %d\n", port, port->port.count); + port->port.count = 1; } - if (--port->count < 0) { - printk(KERN_ERR "gs: gs_close port %p: bad port count: %d\n", port, port->count); - port->count = 0; + if (--port->port.count < 0) { + printk(KERN_ERR "gs: gs_close port %p: bad port count: %d\n", port, port->port.count); + port->port.count = 0; } - if (port->count) { - gs_dprintk(GS_DEBUG_CLOSE, "gs_close port %p: count: %d\n", port, port->count); + if (port->port.count) { + gs_dprintk(GS_DEBUG_CLOSE, "gs_close port %p: count: %d\n", port, port->port.count); spin_unlock_irqrestore(&port->driver_lock, flags); func_exit (); return; } - port->flags |= ASYNC_CLOSING; + port->port.flags |= ASYNC_CLOSING; /* * Now we wait for the transmit buffer to clear; and we notify @@ -585,7 +585,7 @@ void gs_close(struct tty_struct * tty, struct file * filp) if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) gs_wait_tx_flushed (port, port->closing_wait); - port->flags &= ~GS_ACTIVE; + port->port.flags &= ~GS_ACTIVE; gs_flush_buffer(tty); @@ -595,18 +595,18 @@ void gs_close(struct tty_struct * tty, struct file * filp) port->event = 0; port->rd->close (port); port->rd->shutdown_port (port); - port->tty = NULL; + port->port.tty = NULL; - if (port->blocked_open) { + if (port->port.blocked_open) { if (port->close_delay) { spin_unlock_irqrestore(&port->driver_lock, flags); msleep_interruptible(jiffies_to_msecs(port->close_delay)); spin_lock_irqsave(&port->driver_lock, flags); } - wake_up_interruptible(&port->open_wait); + wake_up_interruptible(&port->port.open_wait); } - port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING | ASYNC_INITIALIZED); - wake_up_interruptible(&port->close_wait); + port->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING | ASYNC_INITIALIZED); + wake_up_interruptible(&port->port.close_wait); func_exit (); } @@ -626,10 +626,10 @@ void gs_set_termios (struct tty_struct * tty, port = tty->driver_data; if (!port) return; - if (!port->tty) { + if (!port->port.tty) { /* This seems to happen when this is called after gs_close. */ - gs_dprintk (GS_DEBUG_TERMIOS, "gs: Odd: port->tty is NULL\n"); - port->tty = tty; + gs_dprintk (GS_DEBUG_TERMIOS, "gs: Odd: port->port.tty is NULL\n"); + port->port.tty = tty; } @@ -651,15 +651,15 @@ void gs_set_termios (struct tty_struct * tty, baudrate = tty_get_baud_rate(tty); if ((tiosp->c_cflag & CBAUD) == B38400) { - if ( (port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + if ( (port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) baudrate = 57600; - else if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + else if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) baudrate = 115200; - else if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + else if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) baudrate = 230400; - else if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + else if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) baudrate = 460800; - else if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) + else if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) baudrate = (port->baud_base / port->custom_divisor); } @@ -715,7 +715,7 @@ int gs_init_port(struct gs_port *port) func_enter (); - if (port->flags & ASYNC_INITIALIZED) { + if (port->port.flags & ASYNC_INITIALIZED) { func_exit (); return 0; } @@ -737,15 +737,15 @@ int gs_init_port(struct gs_port *port) } spin_lock_irqsave (&port->driver_lock, flags); - if (port->tty) - clear_bit(TTY_IO_ERROR, &port->tty->flags); + if (port->port.tty) + clear_bit(TTY_IO_ERROR, &port->port.tty->flags); mutex_init(&port->port_write_mutex); port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; spin_unlock_irqrestore(&port->driver_lock, flags); - gs_set_termios(port->tty, NULL); + gs_set_termios(port->port.tty, NULL); spin_lock_irqsave (&port->driver_lock, flags); - port->flags |= ASYNC_INITIALIZED; - port->flags &= ~GS_TX_INTEN; + port->port.flags |= ASYNC_INITIALIZED; + port->port.flags &= ~GS_TX_INTEN; spin_unlock_irqrestore(&port->driver_lock, flags); func_exit (); @@ -764,11 +764,11 @@ int gs_setserial(struct gs_port *port, struct serial_struct __user *sp) if ((sio.baud_base != port->baud_base) || (sio.close_delay != port->close_delay) || ((sio.flags & ~ASYNC_USR_MASK) != - (port->flags & ~ASYNC_USR_MASK))) + (port->port.flags & ~ASYNC_USR_MASK))) return(-EPERM); } - port->flags = (port->flags & ~ASYNC_USR_MASK) | + port->port.flags = (port->port.flags & ~ASYNC_USR_MASK) | (sio.flags & ASYNC_USR_MASK); port->baud_base = sio.baud_base; @@ -776,7 +776,7 @@ int gs_setserial(struct gs_port *port, struct serial_struct __user *sp) port->closing_wait = sio.closing_wait; port->custom_divisor = sio.custom_divisor; - gs_set_termios (port->tty, NULL); + gs_set_termios (port->port.tty, NULL); return 0; } @@ -793,7 +793,7 @@ int gs_getserial(struct gs_port *port, struct serial_struct __user *sp) struct serial_struct sio; memset(&sio, 0, sizeof(struct serial_struct)); - sio.flags = port->flags; + sio.flags = port->port.flags; sio.baud_base = port->baud_base; sio.close_delay = port->close_delay; sio.closing_wait = port->closing_wait; @@ -821,10 +821,10 @@ void gs_got_break(struct gs_port *port) { func_enter (); - tty_insert_flip_char(port->tty, 0, TTY_BREAK); - tty_schedule_flip(port->tty); - if (port->flags & ASYNC_SAK) { - do_SAK (port->tty); + tty_insert_flip_char(port->port.tty, 0, TTY_BREAK); + tty_schedule_flip(port->port.tty); + if (port->port.flags & ASYNC_SAK) { + do_SAK (port->port.tty); } func_exit (); diff --git a/drivers/char/rio/rio_linux.c b/drivers/char/rio/rio_linux.c index e49e6e6372f3..0cdfee152916 100644 --- a/drivers/char/rio/rio_linux.c +++ b/drivers/char/rio/rio_linux.c @@ -431,7 +431,7 @@ static void rio_disable_tx_interrupts(void *ptr) { func_enter(); - /* port->gs.flags &= ~GS_TX_INTEN; */ + /* port->gs.port.flags &= ~GS_TX_INTEN; */ func_exit(); } @@ -455,7 +455,7 @@ static void rio_enable_tx_interrupts(void *ptr) * In general we cannot count on "tx empty" interrupts, although * the interrupt routine seems to be able to tell the difference. */ - PortP->gs.flags &= ~GS_TX_INTEN; + PortP->gs.port.flags &= ~GS_TX_INTEN; func_exit(); } @@ -510,7 +510,7 @@ static void rio_shutdown_port(void *ptr) func_enter(); PortP = (struct Port *) ptr; - PortP->gs.tty = NULL; + PortP->gs.port.tty = NULL; func_exit(); } @@ -529,7 +529,7 @@ static void rio_hungup(void *ptr) func_enter(); PortP = (struct Port *) ptr; - PortP->gs.tty = NULL; + PortP->gs.port.tty = NULL; func_exit(); } @@ -549,12 +549,12 @@ static void rio_close(void *ptr) riotclose(ptr); - if (PortP->gs.count) { - printk(KERN_ERR "WARNING port count:%d\n", PortP->gs.count); - PortP->gs.count = 0; + if (PortP->gs.port.count) { + printk(KERN_ERR "WARNING port count:%d\n", PortP->gs.port.count); + PortP->gs.port.count = 0; } - PortP->gs.tty = NULL; + PortP->gs.port.tty = NULL; func_exit(); } @@ -849,8 +849,8 @@ static int rio_init_datastructures(void) /* * Initializing wait queue */ - init_waitqueue_head(&port->gs.open_wait); - init_waitqueue_head(&port->gs.close_wait); + init_waitqueue_head(&port->gs.port.open_wait); + init_waitqueue_head(&port->gs.port.close_wait); } #else /* We could postpone initializing them to when they are configured. */ diff --git a/drivers/char/rio/riocmd.c b/drivers/char/rio/riocmd.c index 391f0b4da7ea..01f2654d5a2e 100644 --- a/drivers/char/rio/riocmd.c +++ b/drivers/char/rio/riocmd.c @@ -484,12 +484,12 @@ static int RIOCommandRup(struct rio_info *p, uint Rup, struct Host *HostP, struc ** If the device is a modem, then check the modem ** carrier. */ - if (PortP->gs.tty == NULL) + if (PortP->gs.port.tty == NULL) break; - if (PortP->gs.tty->termios == NULL) + if (PortP->gs.port.tty->termios == NULL) break; - if (!(PortP->gs.tty->termios->c_cflag & CLOCAL) && ((PortP->State & (RIO_MOPEN | RIO_WOPEN)))) { + if (!(PortP->gs.port.tty->termios->c_cflag & CLOCAL) && ((PortP->State & (RIO_MOPEN | RIO_WOPEN)))) { rio_dprintk(RIO_DEBUG_CMD, "Is there a Carrier?\n"); /* @@ -506,7 +506,7 @@ static int RIOCommandRup(struct rio_info *p, uint Rup, struct Host *HostP, struc ** wakeup anyone in WOPEN */ if (PortP->State & (PORT_ISOPEN | RIO_WOPEN)) - wake_up_interruptible(&PortP->gs.open_wait); + wake_up_interruptible(&PortP->gs.port.open_wait); } } else { /* @@ -514,7 +514,7 @@ static int RIOCommandRup(struct rio_info *p, uint Rup, struct Host *HostP, struc */ if (PortP->State & RIO_CARR_ON) { if (PortP->State & (PORT_ISOPEN | RIO_WOPEN | RIO_MOPEN)) - tty_hangup(PortP->gs.tty); + tty_hangup(PortP->gs.port.tty); PortP->State &= ~RIO_CARR_ON; rio_dprintk(RIO_DEBUG_CMD, "Carrirer just went down\n"); } diff --git a/drivers/char/rio/riointr.c b/drivers/char/rio/riointr.c index 11c7987821c4..71f87600907c 100644 --- a/drivers/char/rio/riointr.c +++ b/drivers/char/rio/riointr.c @@ -102,7 +102,7 @@ void RIOTxEnable(char *en) PortP = (struct Port *) en; p = (struct rio_info *) PortP->p; - tty = PortP->gs.tty; + tty = PortP->gs.port.tty; rio_dprintk(RIO_DEBUG_INTR, "tx port %d: %d chars queued.\n", PortP->PortNum, PortP->gs.xmit_cnt); @@ -158,7 +158,7 @@ void RIOTxEnable(char *en) rio_spin_unlock_irqrestore(&PortP->portSem, flags); if (PortP->gs.xmit_cnt <= (PortP->gs.wakeup_chars + 2 * PKT_MAX_DATA_LEN)) - tty_wakeup(PortP->gs.tty); + tty_wakeup(PortP->gs.port.tty); } @@ -241,7 +241,7 @@ void RIOServiceHost(struct rio_info *p, struct Host *HostP) ** find corresponding tty structure. The process of mapping ** the ports puts these here. */ - ttyP = PortP->gs.tty; + ttyP = PortP->gs.port.tty; /* ** Lock the port before we begin working on it. @@ -335,7 +335,7 @@ void RIOServiceHost(struct rio_info *p, struct Host *HostP) ** find corresponding tty structure. The process of mapping ** the ports puts these here. */ - ttyP = PortP->gs.tty; + ttyP = PortP->gs.port.tty; /* If ttyP is NULL, the port is getting closed. Forget about it. */ if (!ttyP) { rio_dprintk(RIO_DEBUG_INTR, "no tty, so skipping.\n"); @@ -542,7 +542,7 @@ static void RIOReceive(struct rio_info *p, struct Port *PortP) intCount++; - TtyP = PortP->gs.tty; + TtyP = PortP->gs.port.tty; if (!TtyP) { rio_dprintk(RIO_DEBUG_INTR, "RIOReceive: tty is null. \n"); return; diff --git a/drivers/char/rio/rioparam.c b/drivers/char/rio/rioparam.c index 447ca34a6a72..d687c17be152 100644 --- a/drivers/char/rio/rioparam.c +++ b/drivers/char/rio/rioparam.c @@ -160,7 +160,7 @@ int RIOParam(struct Port *PortP, int cmd, int Modem, int SleepFlag) func_enter(); - TtyP = PortP->gs.tty; + TtyP = PortP->gs.port.tty; rio_dprintk(RIO_DEBUG_PARAM, "RIOParam: Port:%d cmd:%d Modem:%d SleepFlag:%d Mapped: %d, tty=%p\n", PortP->PortNum, cmd, Modem, SleepFlag, PortP->Mapped, TtyP); diff --git a/drivers/char/rio/riotty.c b/drivers/char/rio/riotty.c index 95a88a4138e8..2fb49e89b324 100644 --- a/drivers/char/rio/riotty.c +++ b/drivers/char/rio/riotty.c @@ -140,14 +140,14 @@ int riotopen(struct tty_struct *tty, struct file *filp) tty->driver_data = PortP; - PortP->gs.tty = tty; - PortP->gs.count++; + PortP->gs.port.tty = tty; + PortP->gs.port.count++; rio_dprintk(RIO_DEBUG_TTY, "%d bytes in tx buffer\n", PortP->gs.xmit_cnt); retval = gs_init_port(&PortP->gs); if (retval) { - PortP->gs.count--; + PortP->gs.port.count--; return -ENXIO; } /* @@ -293,7 +293,7 @@ int riotopen(struct tty_struct *tty, struct file *filp) ** insert test for carrier here. -- ??? ** I already see that test here. What's the deal? -- REW */ - if ((PortP->gs.tty->termios->c_cflag & CLOCAL) || + if ((PortP->gs.port.tty->termios->c_cflag & CLOCAL) || (PortP->ModemState & RIOC_MSVR1_CD)) { rio_dprintk(RIO_DEBUG_TTY, "open(%d) Modem carr on\n", SysPort); /* @@ -301,16 +301,16 @@ int riotopen(struct tty_struct *tty, struct file *filp) wakeup((caddr_t) &tp->tm.c_canq); */ PortP->State |= RIO_CARR_ON; - wake_up_interruptible(&PortP->gs.open_wait); + wake_up_interruptible(&PortP->gs.port.open_wait); } else { /* no carrier - wait for DCD */ /* - while (!(PortP->gs.tty->termios->c_state & CARR_ON) && + while (!(PortP->gs.port.tty->termios->c_state & CARR_ON) && !(filp->f_flags & O_NONBLOCK) && !p->RIOHalted ) */ while (!(PortP->State & RIO_CARR_ON) && !(filp->f_flags & O_NONBLOCK) && !p->RIOHalted) { rio_dprintk(RIO_DEBUG_TTY, "open(%d) sleeping for carr on\n", SysPort); /* - PortP->gs.tty->termios->c_state |= WOPEN; + PortP->gs.port.tty->termios->c_state |= WOPEN; */ PortP->State |= RIO_WOPEN; rio_spin_unlock_irqrestore(&PortP->portSem, flags); @@ -380,7 +380,7 @@ int riotclose(void *ptr) /* PortP = p->RIOPortp[SysPort]; */ rio_dprintk(RIO_DEBUG_TTY, "Port is at address %p\n", PortP); /* tp = PortP->TtyP; *//* Get tty */ - tty = PortP->gs.tty; + tty = PortP->gs.port.tty; rio_dprintk(RIO_DEBUG_TTY, "TTY is at address %p\n", tty); if (PortP->gs.closing_wait) diff --git a/drivers/char/sx.c b/drivers/char/sx.c index b1a7a8cb65ea..b1239ee48b78 100644 --- a/drivers/char/sx.c +++ b/drivers/char/sx.c @@ -1,4 +1,3 @@ - /* sx.c -- driver for the Specialix SX series cards. * * This driver will also support the older SI, and XIO cards. @@ -930,7 +929,7 @@ static int sx_set_real_termios(void *ptr) func_enter2(); - if (!port->gs.tty) + if (!port->gs.port.tty) return 0; /* What is this doing here? -- REW @@ -941,19 +940,19 @@ static int sx_set_real_termios(void *ptr) sx_set_baud(port); -#define CFLAG port->gs.tty->termios->c_cflag +#define CFLAG port->gs.port.tty->termios->c_cflag sx_write_channel_byte(port, hi_mr1, - (C_PARENB(port->gs.tty) ? MR1_WITH : MR1_NONE) | - (C_PARODD(port->gs.tty) ? MR1_ODD : MR1_EVEN) | - (C_CRTSCTS(port->gs.tty) ? MR1_RTS_RXFLOW : 0) | + (C_PARENB(port->gs.port.tty) ? MR1_WITH : MR1_NONE) | + (C_PARODD(port->gs.port.tty) ? MR1_ODD : MR1_EVEN) | + (C_CRTSCTS(port->gs.port.tty) ? MR1_RTS_RXFLOW : 0) | (((CFLAG & CSIZE) == CS8) ? MR1_8_BITS : 0) | (((CFLAG & CSIZE) == CS7) ? MR1_7_BITS : 0) | (((CFLAG & CSIZE) == CS6) ? MR1_6_BITS : 0) | (((CFLAG & CSIZE) == CS5) ? MR1_5_BITS : 0)); sx_write_channel_byte(port, hi_mr2, - (C_CRTSCTS(port->gs.tty) ? MR2_CTS_TXFLOW : 0) | - (C_CSTOPB(port->gs.tty) ? MR2_2_STOP : + (C_CRTSCTS(port->gs.port.tty) ? MR2_CTS_TXFLOW : 0) | + (C_CSTOPB(port->gs.port.tty) ? MR2_2_STOP : MR2_1_STOP)); switch (CFLAG & CSIZE) { @@ -976,44 +975,44 @@ static int sx_set_real_termios(void *ptr) } sx_write_channel_byte(port, hi_prtcl, - (I_IXON(port->gs.tty) ? SP_TXEN : 0) | - (I_IXOFF(port->gs.tty) ? SP_RXEN : 0) | - (I_IXANY(port->gs.tty) ? SP_TANY : 0) | SP_DCEN); + (I_IXON(port->gs.port.tty) ? SP_TXEN : 0) | + (I_IXOFF(port->gs.port.tty) ? SP_RXEN : 0) | + (I_IXANY(port->gs.port.tty) ? SP_TANY : 0) | SP_DCEN); sx_write_channel_byte(port, hi_break, - (I_IGNBRK(port->gs.tty) ? BR_IGN : 0 | - I_BRKINT(port->gs.tty) ? BR_INT : 0)); + (I_IGNBRK(port->gs.port.tty) ? BR_IGN : 0 | + I_BRKINT(port->gs.port.tty) ? BR_INT : 0)); - sx_write_channel_byte(port, hi_txon, START_CHAR(port->gs.tty)); - sx_write_channel_byte(port, hi_rxon, START_CHAR(port->gs.tty)); - sx_write_channel_byte(port, hi_txoff, STOP_CHAR(port->gs.tty)); - sx_write_channel_byte(port, hi_rxoff, STOP_CHAR(port->gs.tty)); + sx_write_channel_byte(port, hi_txon, START_CHAR(port->gs.port.tty)); + sx_write_channel_byte(port, hi_rxon, START_CHAR(port->gs.port.tty)); + sx_write_channel_byte(port, hi_txoff, STOP_CHAR(port->gs.port.tty)); + sx_write_channel_byte(port, hi_rxoff, STOP_CHAR(port->gs.port.tty)); sx_reconfigure_port(port); /* Tell line discipline whether we will do input cooking */ - if (I_OTHER(port->gs.tty)) { - clear_bit(TTY_HW_COOK_IN, &port->gs.tty->flags); + if (I_OTHER(port->gs.port.tty)) { + clear_bit(TTY_HW_COOK_IN, &port->gs.port.tty->flags); } else { - set_bit(TTY_HW_COOK_IN, &port->gs.tty->flags); + set_bit(TTY_HW_COOK_IN, &port->gs.port.tty->flags); } sx_dprintk(SX_DEBUG_TERMIOS, "iflags: %x(%d) ", - (unsigned int)port->gs.tty->termios->c_iflag, - I_OTHER(port->gs.tty)); + (unsigned int)port->gs.port.tty->termios->c_iflag, + I_OTHER(port->gs.port.tty)); /* Tell line discipline whether we will do output cooking. * If OPOST is set and no other output flags are set then we can do output * processing. Even if only *one* other flag in the O_OTHER group is set * we do cooking in software. */ - if (O_OPOST(port->gs.tty) && !O_OTHER(port->gs.tty)) { - set_bit(TTY_HW_COOK_OUT, &port->gs.tty->flags); + if (O_OPOST(port->gs.port.tty) && !O_OTHER(port->gs.port.tty)) { + set_bit(TTY_HW_COOK_OUT, &port->gs.port.tty->flags); } else { - clear_bit(TTY_HW_COOK_OUT, &port->gs.tty->flags); + clear_bit(TTY_HW_COOK_OUT, &port->gs.port.tty->flags); } sx_dprintk(SX_DEBUG_TERMIOS, "oflags: %x(%d)\n", - (unsigned int)port->gs.tty->termios->c_oflag, - O_OTHER(port->gs.tty)); + (unsigned int)port->gs.port.tty->termios->c_oflag, + O_OTHER(port->gs.port.tty)); /* port->c_dcd = sx_get_CD (port); */ func_exit(); return 0; @@ -1102,8 +1101,8 @@ static void sx_transmit_chars(struct sx_port *port) sx_disable_tx_interrupts(port); } - if ((port->gs.xmit_cnt <= port->gs.wakeup_chars) && port->gs.tty) { - tty_wakeup(port->gs.tty); + if ((port->gs.xmit_cnt <= port->gs.wakeup_chars) && port->gs.port.tty) { + tty_wakeup(port->gs.port.tty); sx_dprintk(SX_DEBUG_TRANSMIT, "Waking up.... ldisc (%d)....\n", port->gs.wakeup_chars); } @@ -1126,7 +1125,7 @@ static inline void sx_receive_chars(struct sx_port *port) unsigned char *rp; func_enter2(); - tty = port->gs.tty; + tty = port->gs.port.tty; while (1) { rx_op = sx_read_channel_byte(port, hi_rxopos); c = (sx_read_channel_byte(port, hi_rxipos) - rx_op) & 0xff; @@ -1211,12 +1210,12 @@ static inline void sx_check_modem_signals(struct sx_port *port) /* DCD went UP */ if ((sx_read_channel_byte(port, hi_hstat) != HS_IDLE_CLOSED) && - !(port->gs.tty->termios-> + !(port->gs.port.tty->termios-> c_cflag & CLOCAL)) { /* Are we blocking in open? */ sx_dprintk(SX_DEBUG_MODEMSIGNALS, "DCD " "active, unblocking open\n"); - wake_up_interruptible(&port->gs. + wake_up_interruptible(&port->gs.port. open_wait); } else { sx_dprintk(SX_DEBUG_MODEMSIGNALS, "DCD " @@ -1224,10 +1223,10 @@ static inline void sx_check_modem_signals(struct sx_port *port) } } else { /* DCD went down! */ - if (!(port->gs.tty->termios->c_cflag & CLOCAL)){ + if (!(port->gs.port.tty->termios->c_cflag & CLOCAL)){ sx_dprintk(SX_DEBUG_MODEMSIGNALS, "DCD " "dropped. hanging up....\n"); - tty_hangup(port->gs.tty); + tty_hangup(port->gs.port.tty); } else { sx_dprintk(SX_DEBUG_MODEMSIGNALS, "DCD " "dropped. ignoring.\n"); @@ -1325,7 +1324,7 @@ static irqreturn_t sx_interrupt(int irq, void *ptr) for (i = 0; i < board->nports; i++) { port = &board->ports[i]; - if (port->gs.flags & GS_ACTIVE) { + if (port->gs.port.flags & GS_ACTIVE) { if (sx_read_channel_byte(port, hi_state)) { sx_dprintk(SX_DEBUG_INTERRUPTS, "Port %d: " "modem signal change?... \n",i); @@ -1334,7 +1333,7 @@ static irqreturn_t sx_interrupt(int irq, void *ptr) if (port->gs.xmit_cnt) { sx_transmit_chars(port); } - if (!(port->gs.flags & SX_RX_THROTTLE)) { + if (!(port->gs.port.flags & SX_RX_THROTTLE)) { sx_receive_chars(port); } } @@ -1373,7 +1372,7 @@ static void sx_disable_tx_interrupts(void *ptr) struct sx_port *port = ptr; func_enter2(); - port->gs.flags &= ~GS_TX_INTEN; + port->gs.port.flags &= ~GS_TX_INTEN; func_exit(); } @@ -1394,7 +1393,7 @@ static void sx_enable_tx_interrupts(void *ptr) /* XXX Must be "HIGH_WATER" for SI card according to doc. */ if (data_in_buffer < LOW_WATER) - port->gs.flags &= ~GS_TX_INTEN; + port->gs.port.flags &= ~GS_TX_INTEN; func_exit(); } @@ -1442,8 +1441,8 @@ static void sx_shutdown_port(void *ptr) func_enter(); - port->gs.flags &= ~GS_ACTIVE; - if (port->gs.tty && (port->gs.tty->termios->c_cflag & HUPCL)) { + port->gs.port.flags &= ~GS_ACTIVE; + if (port->gs.port.tty && (port->gs.port.tty->termios->c_cflag & HUPCL)) { sx_setsignals(port, 0, 0); sx_reconfigure_port(port); } @@ -1485,8 +1484,8 @@ static int sx_open(struct tty_struct *tty, struct file *filp) spin_lock_irqsave(&port->gs.driver_lock, flags); tty->driver_data = port; - port->gs.tty = tty; - port->gs.count++; + port->gs.port.tty = tty; + port->gs.port.count++; spin_unlock_irqrestore(&port->gs.driver_lock, flags); sx_dprintk(SX_DEBUG_OPEN, "starting port\n"); @@ -1497,12 +1496,12 @@ static int sx_open(struct tty_struct *tty, struct file *filp) retval = gs_init_port(&port->gs); sx_dprintk(SX_DEBUG_OPEN, "done gs_init\n"); if (retval) { - port->gs.count--; + port->gs.port.count--; return retval; } - port->gs.flags |= GS_ACTIVE; - if (port->gs.count <= 1) + port->gs.port.flags |= GS_ACTIVE; + if (port->gs.port.count <= 1) sx_setsignals(port, 1, 1); #if 0 @@ -1513,12 +1512,12 @@ static int sx_open(struct tty_struct *tty, struct file *filp) my_hd_io(port->board->base + port->ch_base, sizeof(*port)); #endif - if (port->gs.count <= 1) { + if (port->gs.port.count <= 1) { if (sx_send_command(port, HS_LOPEN, -1, HS_IDLE_OPEN) != 1) { printk(KERN_ERR "sx: Card didn't respond to LOPEN " "command.\n"); spin_lock_irqsave(&port->gs.driver_lock, flags); - port->gs.count--; + port->gs.port.count--; spin_unlock_irqrestore(&port->gs.driver_lock, flags); return -EIO; } @@ -1526,11 +1525,11 @@ static int sx_open(struct tty_struct *tty, struct file *filp) retval = gs_block_til_ready(port, filp); sx_dprintk(SX_DEBUG_OPEN, "Block til ready returned %d. Count=%d\n", - retval, port->gs.count); + retval, port->gs.port.count); if (retval) { /* - * Don't lower gs.count here because sx_close() will be called later + * Don't lower gs.port.count here because sx_close() will be called later */ return retval; @@ -1571,14 +1570,14 @@ static void sx_close(void *ptr) } sx_dprintk(SX_DEBUG_CLOSE, "waited %d jiffies for close. count=%d\n", - 5 * HZ - to - 1, port->gs.count); + 5 * HZ - to - 1, port->gs.port.count); - if (port->gs.count) { + if (port->gs.port.count) { sx_dprintk(SX_DEBUG_CLOSE, "WARNING port count:%d\n", - port->gs.count); + port->gs.port.count); /*printk("%s SETTING port count to zero: %p count: %d\n", - __func__, port, port->gs.count); - port->gs.count = 0;*/ + __func__, port, port->gs.port.count); + port->gs.port.count = 0;*/ } func_exit(); @@ -1939,7 +1938,7 @@ static void sx_throttle(struct tty_struct *tty) * control then throttle the port. */ if ((tty->termios->c_cflag & CRTSCTS) || (I_IXOFF(tty))) { - port->gs.flags |= SX_RX_THROTTLE; + port->gs.port.flags |= SX_RX_THROTTLE; } func_exit(); } @@ -1953,7 +1952,7 @@ static void sx_unthrottle(struct tty_struct *tty) * this port in case we disabled flow control while the port * was throttled */ - port->gs.flags &= ~SX_RX_THROTTLE; + port->gs.port.flags &= ~SX_RX_THROTTLE; func_exit(); return; } @@ -2408,9 +2407,7 @@ static int sx_init_portstructs(int nboards, int nports) /* * Initializing wait queue */ - init_waitqueue_head(&port->gs.open_wait); - init_waitqueue_head(&port->gs.close_wait); - + tty_port_init(&port->gs.port); port++; } } diff --git a/include/linux/generic_serial.h b/include/linux/generic_serial.h index 110833666e37..4cc913939817 100644 --- a/include/linux/generic_serial.h +++ b/include/linux/generic_serial.h @@ -14,6 +14,7 @@ #ifdef __KERNEL__ #include +#include struct real_driver { void (*disable_tx_interrupts) (void *); @@ -33,17 +34,12 @@ struct real_driver { struct gs_port { int magic; + struct tty_port port; unsigned char *xmit_buf; int xmit_head; int xmit_tail; int xmit_cnt; struct mutex port_write_mutex; - int flags; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; - int count; - int blocked_open; - struct tty_struct *tty; unsigned long event; unsigned short closing_wait; int close_delay; -- cgit v1.2.3 From b02f5ad6a3ff5a1ee2a7b8ec6eee338de553b060 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 16 Jul 2008 21:55:53 +0100 Subject: istallion: use tty_port Switch istallion to use the new tty_port structure Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/istallion.c | 116 +++++++++++++++++++++++----------------------- include/linux/istallion.h | 6 +-- 2 files changed, 59 insertions(+), 63 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index 7c8b62f162bf..6ef1c565705c 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -735,8 +735,8 @@ static void stli_cleanup_ports(struct stlibrd *brdp) for (j = 0; j < STL_MAXPORTS; j++) { portp = brdp->ports[j]; if (portp != NULL) { - if (portp->tty != NULL) - tty_hangup(portp->tty); + if (portp->port.tty != NULL) + tty_hangup(portp->port.tty); kfree(portp); } } @@ -811,9 +811,9 @@ static int stli_open(struct tty_struct *tty, struct file *filp) * The sleep here does not need interrupt protection since the wakeup * for it is done with the same context. */ - if (portp->flags & ASYNC_CLOSING) { - interruptible_sleep_on(&portp->close_wait); - if (portp->flags & ASYNC_HUP_NOTIFY) + if (portp->port.flags & ASYNC_CLOSING) { + interruptible_sleep_on(&portp->port.close_wait); + if (portp->port.flags & ASYNC_HUP_NOTIFY) return -EAGAIN; return -ERESTARTSYS; } @@ -824,7 +824,7 @@ static int stli_open(struct tty_struct *tty, struct file *filp) * requires several commands to the board we will need to wait for any * other open that is already initializing the port. */ - portp->tty = tty; + portp->port.tty = tty; tty->driver_data = portp; portp->refcount++; @@ -833,10 +833,10 @@ static int stli_open(struct tty_struct *tty, struct file *filp) if (signal_pending(current)) return -ERESTARTSYS; - if ((portp->flags & ASYNC_INITIALIZED) == 0) { + if ((portp->port.flags & ASYNC_INITIALIZED) == 0) { set_bit(ST_INITIALIZING, &portp->state); if ((rc = stli_initopen(brdp, portp)) >= 0) { - portp->flags |= ASYNC_INITIALIZED; + portp->port.flags |= ASYNC_INITIALIZED; clear_bit(TTY_IO_ERROR, &tty->flags); } clear_bit(ST_INITIALIZING, &portp->state); @@ -851,9 +851,9 @@ static int stli_open(struct tty_struct *tty, struct file *filp) * The sleep here does not need interrupt protection since the wakeup * for it is done with the same context. */ - if (portp->flags & ASYNC_CLOSING) { - interruptible_sleep_on(&portp->close_wait); - if (portp->flags & ASYNC_HUP_NOTIFY) + if (portp->port.flags & ASYNC_CLOSING) { + interruptible_sleep_on(&portp->port.close_wait); + if (portp->port.flags & ASYNC_HUP_NOTIFY) return -EAGAIN; return -ERESTARTSYS; } @@ -867,7 +867,7 @@ static int stli_open(struct tty_struct *tty, struct file *filp) if ((rc = stli_waitcarrier(brdp, portp, filp)) != 0) return rc; } - portp->flags |= ASYNC_NORMAL_ACTIVE; + portp->port.flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -895,7 +895,7 @@ static void stli_close(struct tty_struct *tty, struct file *filp) return; } - portp->flags |= ASYNC_CLOSING; + portp->port.flags |= ASYNC_CLOSING; /* * May want to wait for data to drain before closing. The BUSY flag @@ -911,7 +911,7 @@ static void stli_close(struct tty_struct *tty, struct file *filp) if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE) tty_wait_until_sent(tty, portp->closing_wait); - portp->flags &= ~ASYNC_INITIALIZED; + portp->port.flags &= ~ASYNC_INITIALIZED; brdp = stli_brds[portp->brdnr]; stli_rawclose(brdp, portp, 0, 0); if (tty->termios->c_cflag & HUPCL) { @@ -931,16 +931,16 @@ static void stli_close(struct tty_struct *tty, struct file *filp) stli_flushbuffer(tty); tty->closing = 0; - portp->tty = NULL; + portp->port.tty = NULL; if (portp->openwaitcnt) { if (portp->close_delay) msleep_interruptible(jiffies_to_msecs(portp->close_delay)); - wake_up_interruptible(&portp->open_wait); + wake_up_interruptible(&portp->port.open_wait); } - portp->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&portp->close_wait); + portp->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); + wake_up_interruptible(&portp->port.close_wait); } /*****************************************************************************/ @@ -970,7 +970,7 @@ static int stli_initopen(struct stlibrd *brdp, struct stliport *portp) sizeof(asynotify_t), 0)) < 0) return rc; - tty = portp->tty; + tty = portp->port.tty; if (tty == NULL) return -ENODEV; stli_mkasyport(portp, &aport, tty->termios); @@ -1169,7 +1169,7 @@ static int stli_setport(struct stliport *portp) if (portp == NULL) return -ENODEV; - if (portp->tty == NULL) + if (portp->port.tty == NULL) return -ENODEV; if (portp->brdnr >= stli_nrbrds) return -ENODEV; @@ -1177,7 +1177,7 @@ static int stli_setport(struct stliport *portp) if (brdp == NULL) return -ENODEV; - stli_mkasyport(portp, &aport, portp->tty->termios); + stli_mkasyport(portp, &aport, portp->port.tty->termios); return(stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0)); } @@ -1196,7 +1196,7 @@ static int stli_waitcarrier(struct stlibrd *brdp, struct stliport *portp, struct rc = 0; doclocal = 0; - if (portp->tty->termios->c_cflag & CLOCAL) + if (portp->port.tty->termios->c_cflag & CLOCAL) doclocal++; spin_lock_irqsave(&stli_lock, flags); @@ -1211,14 +1211,14 @@ static int stli_waitcarrier(struct stlibrd *brdp, struct stliport *portp, struct &portp->asig, sizeof(asysigs_t), 0)) < 0) break; if (tty_hung_up_p(filp) || - ((portp->flags & ASYNC_INITIALIZED) == 0)) { - if (portp->flags & ASYNC_HUP_NOTIFY) + ((portp->port.flags & ASYNC_INITIALIZED) == 0)) { + if (portp->port.flags & ASYNC_HUP_NOTIFY) rc = -EBUSY; else rc = -ERESTARTSYS; break; } - if (((portp->flags & ASYNC_CLOSING) == 0) && + if (((portp->port.flags & ASYNC_CLOSING) == 0) && (doclocal || (portp->sigs & TIOCM_CD))) { break; } @@ -1226,7 +1226,7 @@ static int stli_waitcarrier(struct stlibrd *brdp, struct stliport *portp, struct rc = -ERESTARTSYS; break; } - interruptible_sleep_on(&portp->open_wait); + interruptible_sleep_on(&portp->port.open_wait); } spin_lock_irqsave(&stli_lock, flags); @@ -1548,7 +1548,7 @@ static int stli_getserial(struct stliport *portp, struct serial_struct __user *s sio.type = PORT_UNKNOWN; sio.line = portp->portnr; sio.irq = 0; - sio.flags = portp->flags; + sio.flags = portp->port.flags; sio.baud_base = portp->baud_base; sio.close_delay = portp->close_delay; sio.closing_wait = portp->closing_wait; @@ -1583,11 +1583,11 @@ static int stli_setserial(struct stliport *portp, struct serial_struct __user *s if ((sio.baud_base != portp->baud_base) || (sio.close_delay != portp->close_delay) || ((sio.flags & ~ASYNC_USR_MASK) != - (portp->flags & ~ASYNC_USR_MASK))) + (portp->port.flags & ~ASYNC_USR_MASK))) return -EPERM; } - portp->flags = (portp->flags & ~ASYNC_USR_MASK) | + portp->port.flags = (portp->port.flags & ~ASYNC_USR_MASK) | (sio.flags & ASYNC_USR_MASK); portp->baud_base = sio.baud_base; portp->close_delay = sio.close_delay; @@ -1751,7 +1751,7 @@ static void stli_settermios(struct tty_struct *tty, struct ktermios *old) if ((old->c_cflag & CRTSCTS) && ((tiosp->c_cflag & CRTSCTS) == 0)) tty->hw_stopped = 0; if (((old->c_cflag & CLOCAL) == 0) && (tiosp->c_cflag & CLOCAL)) - wake_up_interruptible(&portp->open_wait); + wake_up_interruptible(&portp->port.open_wait); } /*****************************************************************************/ @@ -1834,7 +1834,7 @@ static void stli_hangup(struct tty_struct *tty) if (brdp == NULL) return; - portp->flags &= ~ASYNC_INITIALIZED; + portp->port.flags &= ~ASYNC_INITIALIZED; if (!test_bit(ST_CLOSING, &portp->state)) stli_rawclose(brdp, portp, 0, 0); @@ -1855,12 +1855,12 @@ static void stli_hangup(struct tty_struct *tty) clear_bit(ST_TXBUSY, &portp->state); clear_bit(ST_RXSTOP, &portp->state); set_bit(TTY_IO_ERROR, &tty->flags); - portp->tty = NULL; - portp->flags &= ~ASYNC_NORMAL_ACTIVE; + portp->port.tty = NULL; + portp->port.flags &= ~ASYNC_NORMAL_ACTIVE; portp->refcount = 0; spin_unlock_irqrestore(&stli_lock, flags); - wake_up_interruptible(&portp->open_wait); + wake_up_interruptible(&portp->port.open_wait); } /*****************************************************************************/ @@ -2188,7 +2188,7 @@ static void stli_read(struct stlibrd *brdp, struct stliport *portp) if (test_bit(ST_RXSTOP, &portp->state)) return; - tty = portp->tty; + tty = portp->port.tty; if (tty == NULL) return; @@ -2362,7 +2362,7 @@ static int stli_hostcmd(struct stlibrd *brdp, struct stliport *portp) if (ap->notify) { nt = ap->changed; ap->notify = 0; - tty = portp->tty; + tty = portp->port.tty; if (nt.signal & SG_DCD) { oldsigs = portp->sigs; @@ -2370,10 +2370,10 @@ static int stli_hostcmd(struct stlibrd *brdp, struct stliport *portp) clear_bit(ST_GETSIGS, &portp->state); if ((portp->sigs & TIOCM_CD) && ((oldsigs & TIOCM_CD) == 0)) - wake_up_interruptible(&portp->open_wait); + wake_up_interruptible(&portp->port.open_wait); if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0)) { - if (portp->flags & ASYNC_CHECK_CD) { + if (portp->port.flags & ASYNC_CHECK_CD) { if (tty) tty_hangup(tty); } @@ -2392,7 +2392,7 @@ static int stli_hostcmd(struct stlibrd *brdp, struct stliport *portp) if ((nt.data & DT_RXBREAK) && (portp->rxmarkmsk & BRKINT)) { if (tty != NULL) { tty_insert_flip_char(tty, 0, TTY_BREAK); - if (portp->flags & ASYNC_SAK) { + if (portp->port.flags & ASYNC_SAK) { do_SAK(tty); EBRDENABLE(brdp); } @@ -2542,17 +2542,17 @@ static void stli_mkasyport(struct stliport *portp, asyport_t *pp, struct ktermio /* * Start of by setting the baud, char size, parity and stop bit info. */ - pp->baudout = tty_get_baud_rate(portp->tty); + pp->baudout = tty_get_baud_rate(portp->port.tty); if ((tiosp->c_cflag & CBAUD) == B38400) { - if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) pp->baudout = 57600; - else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) pp->baudout = 115200; - else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) pp->baudout = 230400; - else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) pp->baudout = 460800; - else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) pp->baudout = (portp->baud_base / portp->custom_divisor); } if (pp->baudout > STL_MAXBAUD) @@ -2625,9 +2625,9 @@ static void stli_mkasyport(struct stliport *portp, asyport_t *pp, struct ktermio * Set up clocal processing as required. */ if (tiosp->c_cflag & CLOCAL) - portp->flags &= ~ASYNC_CHECK_CD; + portp->port.flags &= ~ASYNC_CHECK_CD; else - portp->flags |= ASYNC_CHECK_CD; + portp->port.flags |= ASYNC_CHECK_CD; /* * Transfer any persistent flags into the asyport structure. @@ -2703,8 +2703,8 @@ static int stli_initports(struct stlibrd *brdp) portp->baud_base = STL_BAUDBASE; portp->close_delay = STL_CLOSEDELAY; portp->closing_wait = 30 * HZ; - init_waitqueue_head(&portp->open_wait); - init_waitqueue_head(&portp->close_wait); + init_waitqueue_head(&portp->port.open_wait); + init_waitqueue_head(&portp->port.close_wait); init_waitqueue_head(&portp->raw_wait); panelport++; if (panelport >= brdp->panels[panelnr]) { @@ -4246,18 +4246,18 @@ static int stli_portcmdstats(struct stliport *portp) stli_comstats.panel = portp->panelnr; stli_comstats.port = portp->portnr; stli_comstats.state = portp->state; - stli_comstats.flags = portp->flags; + stli_comstats.flags = portp->port.flag; spin_lock_irqsave(&brd_lock, flags); - if (portp->tty != NULL) { - if (portp->tty->driver_data == portp) { - stli_comstats.ttystate = portp->tty->flags; + if (portp->port.tty != NULL) { + if (portp->port.tty->driver_data == portp) { + stli_comstats.ttystate = portp->port.tty->flags; stli_comstats.rxbuffered = -1; - if (portp->tty->termios != NULL) { - stli_comstats.cflags = portp->tty->termios->c_cflag; - stli_comstats.iflags = portp->tty->termios->c_iflag; - stli_comstats.oflags = portp->tty->termios->c_oflag; - stli_comstats.lflags = portp->tty->termios->c_lflag; + if (portp->port.tty->termios != NULL) { + stli_comstats.cflags = portp->port.tty->termios->c_cflag; + stli_comstats.iflags = portp->port.tty->termios->c_iflag; + stli_comstats.oflags = portp->port.tty->termios->c_oflag; + stli_comstats.lflags = portp->port.tty->termios->c_lflag; } } } diff --git a/include/linux/istallion.h b/include/linux/istallion.h index 5a84fe944b74..0d1840723249 100644 --- a/include/linux/istallion.h +++ b/include/linux/istallion.h @@ -51,25 +51,21 @@ */ struct stliport { unsigned long magic; + struct tty_port port; unsigned int portnr; unsigned int panelnr; unsigned int brdnr; unsigned long state; unsigned int devnr; - int flags; int baud_base; int custom_divisor; int close_delay; int closing_wait; - int refcount; int openwaitcnt; int rc; int argsize; void *argp; unsigned int rxmarkmsk; - struct tty_struct *tty; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; wait_queue_head_t raw_wait; struct asysigs asig; unsigned long addr; -- cgit v1.2.3 From f8ae47641611fcdf175ab8bbe89054731b16971d Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 16 Jul 2008 21:56:37 +0100 Subject: stallion: use tty_port Switch the stallion driver to use the tty_port structure Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/stallion.c | 160 +++++++++++++++++++++++------------------------ include/linux/stallion.h | 6 +- 2 files changed, 81 insertions(+), 85 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index d17be10c5d21..0243efb0be95 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -613,17 +613,17 @@ static void stl_cd_change(struct stlport *portp) { unsigned int oldsigs = portp->sigs; - if (!portp->tty) + if (!portp->port.tty) return; portp->sigs = stl_getsignals(portp); if ((portp->sigs & TIOCM_CD) && ((oldsigs & TIOCM_CD) == 0)) - wake_up_interruptible(&portp->open_wait); + wake_up_interruptible(&portp->port.open_wait); if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0)) - if (portp->flags & ASYNC_CHECK_CD) - tty_hangup(portp->tty); + if (portp->port.flags & ASYNC_CHECK_CD) + tty_hangup(portp->port.tty); } /* @@ -734,11 +734,11 @@ static int stl_open(struct tty_struct *tty, struct file *filp) * On the first open of the device setup the port hardware, and * initialize the per port data structure. */ - portp->tty = tty; + portp->port.tty = tty; tty->driver_data = portp; - portp->refcount++; + portp->port.count++; - if ((portp->flags & ASYNC_INITIALIZED) == 0) { + if ((portp->port.flags & ASYNC_INITIALIZED) == 0) { if (!portp->tx.buf) { portp->tx.buf = kmalloc(STL_TXBUFSIZE, GFP_KERNEL); if (!portp->tx.buf) @@ -752,7 +752,7 @@ static int stl_open(struct tty_struct *tty, struct file *filp) stl_enablerxtx(portp, 1, 1); stl_startrxtx(portp, 1, 0); clear_bit(TTY_IO_ERROR, &tty->flags); - portp->flags |= ASYNC_INITIALIZED; + portp->port.flags |= ASYNC_INITIALIZED; } /* @@ -761,9 +761,9 @@ static int stl_open(struct tty_struct *tty, struct file *filp) * The sleep here does not need interrupt protection since the wakeup * for it is done with the same context. */ - if (portp->flags & ASYNC_CLOSING) { - interruptible_sleep_on(&portp->close_wait); - if (portp->flags & ASYNC_HUP_NOTIFY) + if (portp->port.flags & ASYNC_CLOSING) { + interruptible_sleep_on(&portp->port.close_wait); + if (portp->port.flags & ASYNC_HUP_NOTIFY) return -EAGAIN; return -ERESTARTSYS; } @@ -777,7 +777,7 @@ static int stl_open(struct tty_struct *tty, struct file *filp) if ((rc = stl_waitcarrier(portp, filp)) != 0) return rc; - portp->flags |= ASYNC_NORMAL_ACTIVE; + portp->port.flags |= ASYNC_NORMAL_ACTIVE; return 0; } @@ -801,25 +801,25 @@ static int stl_waitcarrier(struct stlport *portp, struct file *filp) spin_lock_irqsave(&stallion_lock, flags); - if (portp->tty->termios->c_cflag & CLOCAL) + if (portp->port.tty->termios->c_cflag & CLOCAL) doclocal++; portp->openwaitcnt++; if (! tty_hung_up_p(filp)) - portp->refcount--; + portp->port.count--; for (;;) { /* Takes brd_lock internally */ stl_setsignals(portp, 1, 1); if (tty_hung_up_p(filp) || - ((portp->flags & ASYNC_INITIALIZED) == 0)) { - if (portp->flags & ASYNC_HUP_NOTIFY) + ((portp->port.flags & ASYNC_INITIALIZED) == 0)) { + if (portp->port.flags & ASYNC_HUP_NOTIFY) rc = -EBUSY; else rc = -ERESTARTSYS; break; } - if (((portp->flags & ASYNC_CLOSING) == 0) && + if (((portp->port.flags & ASYNC_CLOSING) == 0) && (doclocal || (portp->sigs & TIOCM_CD))) break; if (signal_pending(current)) { @@ -827,11 +827,11 @@ static int stl_waitcarrier(struct stlport *portp, struct file *filp) break; } /* FIXME */ - interruptible_sleep_on(&portp->open_wait); + interruptible_sleep_on(&portp->port.open_wait); } if (! tty_hung_up_p(filp)) - portp->refcount++; + portp->port.count++; portp->openwaitcnt--; spin_unlock_irqrestore(&stallion_lock, flags); @@ -904,15 +904,15 @@ static void stl_close(struct tty_struct *tty, struct file *filp) spin_unlock_irqrestore(&stallion_lock, flags); return; } - if ((tty->count == 1) && (portp->refcount != 1)) - portp->refcount = 1; - if (portp->refcount-- > 1) { + if ((tty->count == 1) && (portp->port.count != 1)) + portp->port.count = 1; + if (portp->port.count-- > 1) { spin_unlock_irqrestore(&stallion_lock, flags); return; } - portp->refcount = 0; - portp->flags |= ASYNC_CLOSING; + portp->port.count = 0; + portp->port.flags |= ASYNC_CLOSING; /* * May want to wait for any data to drain before closing. The BUSY @@ -930,7 +930,7 @@ static void stl_close(struct tty_struct *tty, struct file *filp) spin_lock_irqsave(&stallion_lock, flags); - portp->flags &= ~ASYNC_INITIALIZED; + portp->port.flags &= ~ASYNC_INITIALIZED; spin_unlock_irqrestore(&stallion_lock, flags); stl_disableintrs(portp); @@ -949,16 +949,16 @@ static void stl_close(struct tty_struct *tty, struct file *filp) tty_ldisc_flush(tty); tty->closing = 0; - portp->tty = NULL; + portp->port.tty = NULL; if (portp->openwaitcnt) { if (portp->close_delay) msleep_interruptible(jiffies_to_msecs(portp->close_delay)); - wake_up_interruptible(&portp->open_wait); + wake_up_interruptible(&portp->port.open_wait); } - portp->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&portp->close_wait); + portp->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); + wake_up_interruptible(&portp->port.close_wait); } /*****************************************************************************/ @@ -1153,7 +1153,7 @@ static int stl_getserial(struct stlport *portp, struct serial_struct __user *sp) memset(&sio, 0, sizeof(struct serial_struct)); sio.line = portp->portnr; sio.port = portp->ioaddr; - sio.flags = portp->flags; + sio.flags = portp->port.flags; sio.baud_base = portp->baud_base; sio.close_delay = portp->close_delay; sio.closing_wait = portp->closing_wait; @@ -1194,17 +1194,17 @@ static int stl_setserial(struct stlport *portp, struct serial_struct __user *sp) if ((sio.baud_base != portp->baud_base) || (sio.close_delay != portp->close_delay) || ((sio.flags & ~ASYNC_USR_MASK) != - (portp->flags & ~ASYNC_USR_MASK))) + (portp->port.flags & ~ASYNC_USR_MASK))) return -EPERM; } - portp->flags = (portp->flags & ~ASYNC_USR_MASK) | + portp->port.flags = (portp->port.flags & ~ASYNC_USR_MASK) | (sio.flags & ASYNC_USR_MASK); portp->baud_base = sio.baud_base; portp->close_delay = sio.close_delay; portp->closing_wait = sio.closing_wait; portp->custom_divisor = sio.custom_divisor; - stl_setport(portp, portp->tty->termios); + stl_setport(portp, portp->port.tty->termios); return 0; } @@ -1353,7 +1353,7 @@ static void stl_settermios(struct tty_struct *tty, struct ktermios *old) stl_start(tty); } if (((old->c_cflag & CLOCAL) == 0) && (tiosp->c_cflag & CLOCAL)) - wake_up_interruptible(&portp->open_wait); + wake_up_interruptible(&portp->port.open_wait); } /*****************************************************************************/ @@ -1438,7 +1438,7 @@ static void stl_hangup(struct tty_struct *tty) if (portp == NULL) return; - portp->flags &= ~ASYNC_INITIALIZED; + portp->port.flags &= ~ASYNC_INITIALIZED; stl_disableintrs(portp); if (tty->termios->c_cflag & HUPCL) stl_setsignals(portp, 0, 0); @@ -1452,10 +1452,10 @@ static void stl_hangup(struct tty_struct *tty) portp->tx.head = NULL; portp->tx.tail = NULL; } - portp->tty = NULL; - portp->flags &= ~ASYNC_NORMAL_ACTIVE; - portp->refcount = 0; - wake_up_interruptible(&portp->open_wait); + portp->port.tty = NULL; + portp->port.flags &= ~ASYNC_NORMAL_ACTIVE; + portp->port.count = 0; + wake_up_interruptible(&portp->port.open_wait); } /*****************************************************************************/ @@ -1814,8 +1814,8 @@ static int __devinit stl_initports(struct stlbrd *brdp, struct stlpanel *panelp) portp->baud_base = STL_BAUDBASE; portp->close_delay = STL_CLOSEDELAY; portp->closing_wait = 30 * HZ; - init_waitqueue_head(&portp->open_wait); - init_waitqueue_head(&portp->close_wait); + init_waitqueue_head(&portp->port.open_wait); + init_waitqueue_head(&portp->port.close_wait); portp->stats.brd = portp->brdnr; portp->stats.panel = portp->panelnr; portp->stats.port = portp->portnr; @@ -1840,8 +1840,8 @@ static void stl_cleanup_panels(struct stlbrd *brdp) portp = panelp->ports[k]; if (portp == NULL) continue; - if (portp->tty != NULL) - stl_hangup(portp->tty); + if (portp->port.tty != NULL) + stl_hangup(portp->port.tty); kfree(portp->tx.buf); kfree(portp); } @@ -2513,7 +2513,7 @@ static int stl_getportstats(struct stlport *portp, comstats_t __user *cp) } portp->stats.state = portp->istate; - portp->stats.flags = portp->flags; + portp->stats.flags = portp->port.flags; portp->stats.hwid = portp->hwid; portp->stats.ttystate = 0; @@ -2524,16 +2524,16 @@ static int stl_getportstats(struct stlport *portp, comstats_t __user *cp) portp->stats.rxbuffered = 0; spin_lock_irqsave(&stallion_lock, flags); - if (portp->tty != NULL) - if (portp->tty->driver_data == portp) { - portp->stats.ttystate = portp->tty->flags; + if (portp->port.tty != NULL) + if (portp->port.tty->driver_data == portp) { + portp->stats.ttystate = portp->port.tty->flags; /* No longer available as a statistic */ - portp->stats.rxbuffered = 1; /*portp->tty->flip.count; */ - if (portp->tty->termios != NULL) { - portp->stats.cflags = portp->tty->termios->c_cflag; - portp->stats.iflags = portp->tty->termios->c_iflag; - portp->stats.oflags = portp->tty->termios->c_oflag; - portp->stats.lflags = portp->tty->termios->c_lflag; + portp->stats.rxbuffered = 1; /*portp->port.tty->flip.count; */ + if (portp->port.tty->termios != NULL) { + portp->stats.cflags = portp->port.tty->termios->c_cflag; + portp->stats.iflags = portp->port.tty->termios->c_iflag; + portp->stats.oflags = portp->port.tty->termios->c_oflag; + portp->stats.lflags = portp->port.tty->termios->c_lflag; } } spin_unlock_irqrestore(&stallion_lock, flags); @@ -2939,15 +2939,15 @@ static void stl_cd1400setport(struct stlport *portp, struct ktermios *tiosp) } baudrate = stl_baudrates[baudrate]; if ((tiosp->c_cflag & CBAUD) == B38400) { - if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) baudrate = 57600; - else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) baudrate = 115200; - else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) baudrate = 230400; - else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) baudrate = 460800; - else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) baudrate = (portp->baud_base / portp->custom_divisor); } if (baudrate > STL_CD1400MAXBAUD) @@ -2969,9 +2969,9 @@ static void stl_cd1400setport(struct stlport *portp, struct ktermios *tiosp) mcor1 |= MCOR1_DCD; mcor2 |= MCOR2_DCD; sreron |= SRER_MODEM; - portp->flags |= ASYNC_CHECK_CD; + portp->port.flags |= ASYNC_CHECK_CD; } else - portp->flags &= ~ASYNC_CHECK_CD; + portp->port.flags &= ~ASYNC_CHECK_CD; /* * Setup cd1400 enhanced modes if we can. In particular we want to @@ -3242,7 +3242,7 @@ static void stl_cd1400flowctrl(struct stlport *portp, int state) if (portp == NULL) return; - tty = portp->tty; + tty = portp->port.tty; if (tty == NULL) return; @@ -3304,7 +3304,7 @@ static void stl_cd1400sendflow(struct stlport *portp, int state) if (portp == NULL) return; - tty = portp->tty; + tty = portp->port.tty; if (tty == NULL) return; @@ -3503,8 +3503,8 @@ static void stl_cd1400txisr(struct stlpanel *panelp, int ioaddr) if ((len == 0) || ((len < STL_TXBUFLOW) && (test_bit(ASYI_TXLOW, &portp->istate) == 0))) { set_bit(ASYI_TXLOW, &portp->istate); - if (portp->tty) - tty_wakeup(portp->tty); + if (portp->port.tty) + tty_wakeup(portp->port.tty); } if (len == 0) { @@ -3568,7 +3568,7 @@ static void stl_cd1400rxisr(struct stlpanel *panelp, int ioaddr) return; } portp = panelp->ports[(ioack >> 3)]; - tty = portp->tty; + tty = portp->port.tty; if ((ioack & ACK_TYPMASK) == ACK_TYPRXGOOD) { outb((RDCR + portp->uartaddr), ioaddr); @@ -3613,7 +3613,7 @@ static void stl_cd1400rxisr(struct stlpanel *panelp, int ioaddr) if (portp->rxmarkmsk & status) { if (status & ST_BREAK) { status = TTY_BREAK; - if (portp->flags & ASYNC_SAK) { + if (portp->port.flags & ASYNC_SAK) { do_SAK(tty); BRDENABLE(portp->brdnr, portp->pagenr); } @@ -3899,15 +3899,15 @@ static void stl_sc26198setport(struct stlport *portp, struct ktermios *tiosp) } baudrate = stl_baudrates[baudrate]; if ((tiosp->c_cflag & CBAUD) == B38400) { - if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) baudrate = 57600; - else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) baudrate = 115200; - else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) baudrate = 230400; - else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) baudrate = 460800; - else if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) + else if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) baudrate = (portp->baud_base / portp->custom_divisor); } if (baudrate > STL_SC26198MAXBAUD) @@ -3922,11 +3922,11 @@ static void stl_sc26198setport(struct stlport *portp, struct ktermios *tiosp) * Check what form of modem signaling is required and set it up. */ if (tiosp->c_cflag & CLOCAL) { - portp->flags &= ~ASYNC_CHECK_CD; + portp->port.flags &= ~ASYNC_CHECK_CD; } else { iopr |= IOPR_DCDCOS; imron |= IR_IOPORT; - portp->flags |= ASYNC_CHECK_CD; + portp->port.flags |= ASYNC_CHECK_CD; } /* @@ -4174,7 +4174,7 @@ static void stl_sc26198flowctrl(struct stlport *portp, int state) if (portp == NULL) return; - tty = portp->tty; + tty = portp->port.tty; if (tty == NULL) return; @@ -4243,7 +4243,7 @@ static void stl_sc26198sendflow(struct stlport *portp, int state) if (portp == NULL) return; - tty = portp->tty; + tty = portp->port.tty; if (tty == NULL) return; @@ -4421,8 +4421,8 @@ static void stl_sc26198txisr(struct stlport *portp) if ((len == 0) || ((len < STL_TXBUFLOW) && (test_bit(ASYI_TXLOW, &portp->istate) == 0))) { set_bit(ASYI_TXLOW, &portp->istate); - if (portp->tty) - tty_wakeup(portp->tty); + if (portp->port.tty) + tty_wakeup(portp->port.tty); } if (len == 0) { @@ -4475,7 +4475,7 @@ static void stl_sc26198rxisr(struct stlport *portp, unsigned int iack) pr_debug("stl_sc26198rxisr(portp=%p,iack=%x)\n", portp, iack); - tty = portp->tty; + tty = portp->port.tty; ioaddr = portp->ioaddr; outb(GIBCR, (ioaddr + XP_ADDR)); len = inb(ioaddr + XP_DATA) + 1; @@ -4527,7 +4527,7 @@ static void stl_sc26198rxbadch(struct stlport *portp, unsigned char status, char struct tty_struct *tty; unsigned int ioaddr; - tty = portp->tty; + tty = portp->port.tty; ioaddr = portp->ioaddr; if (status & SR_RXPARITY) @@ -4544,7 +4544,7 @@ static void stl_sc26198rxbadch(struct stlport *portp, unsigned char status, char if (portp->rxmarkmsk & status) { if (status & SR_RXBREAK) { status = TTY_BREAK; - if (portp->flags & ASYNC_SAK) { + if (portp->port.flags & ASYNC_SAK) { do_SAK(tty); BRDENABLE(portp->brdnr, portp->pagenr); } diff --git a/include/linux/stallion.h b/include/linux/stallion.h index 0424d75a5aaa..336af33c6ea4 100644 --- a/include/linux/stallion.h +++ b/include/linux/stallion.h @@ -69,6 +69,7 @@ struct stlrq { */ struct stlport { unsigned long magic; + struct tty_port port; unsigned int portnr; unsigned int panelnr; unsigned int brdnr; @@ -76,12 +77,10 @@ struct stlport { int uartaddr; unsigned int pagenr; unsigned long istate; - int flags; int baud_base; int custom_divisor; int close_delay; int closing_wait; - int refcount; int openwaitcnt; int brklen; unsigned int sigs; @@ -92,9 +91,6 @@ struct stlport { unsigned long clk; unsigned long hwid; void *uartp; - struct tty_struct *tty; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; comstats_t stats; struct stlrq tx; }; -- cgit v1.2.3 From 77451e53e0a509a98eda272567869cfe96431ba9 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 16 Jul 2008 21:57:02 +0100 Subject: cyclades: use tty_port Switch cyclades to use the new tty_port structure Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/cyclades.c | 315 +++++++++++++++++++++++------------------------ include/linux/cyclades.h | 7 +- 2 files changed, 158 insertions(+), 164 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index a957dbcc5a46..0144b19d1036 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -762,7 +762,7 @@ static int cy_next_channel; /* next minor available */ /* * This is used to look up the divisor speeds and the timeouts * We're normally limited to 15 distinct baud rates. The extra - * are accessed via settings in info->flags. + * are accessed via settings in info->port.flags. * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, * 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, * HI VHI @@ -1003,7 +1003,7 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, cy_writeb(base_addr + (CyCAR << index), save_xir); /* if there is nowhere to put the data, discard it */ - if (info->tty == NULL) { + if (info->port.tty == NULL) { if ((readb(base_addr + (CyRIVR << index)) & CyIVRMask) == CyIVRRxEx) { /* exception */ data = readb(base_addr + (CyRDSR << index)); @@ -1015,7 +1015,7 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, goto end; } /* there is an open port for this data */ - tty = info->tty; + tty = info->port.tty; if ((readb(base_addr + (CyRIVR << index)) & CyIVRMask) == CyIVRRxEx) { /* exception */ data = readb(base_addr + (CyRDSR << index)); @@ -1041,7 +1041,7 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, readb(base_addr + (CyRDSR << index)), TTY_BREAK); info->icount.rx++; - if (info->flags & ASYNC_SAK) + if (info->port.flags & ASYNC_SAK) do_SAK(tty); } else if (data & CyFRAME) { tty_insert_flip_char(tty, @@ -1145,7 +1145,7 @@ static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, goto end; } info = &cinfo->ports[channel + chip * 4]; - if (info->tty == NULL) { + if (info->port.tty == NULL) { cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) & ~CyTxRdy); goto end; @@ -1190,13 +1190,13 @@ static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, } goto done; } - if (info->xmit_buf == NULL) { + if (info->port.xmit_buf == NULL) { cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) & ~CyTxRdy); goto done; } - if (info->tty->stopped || info->tty->hw_stopped) { + if (info->port.tty->stopped || info->port.tty->hw_stopped) { cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) & ~CyTxRdy); @@ -1211,7 +1211,7 @@ static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, * character. This is necessary because there may not be room * for the two chars needed to send a NULL.) */ - outch = info->xmit_buf[info->xmit_tail]; + outch = info->port.xmit_buf[info->xmit_tail]; if (outch) { info->xmit_cnt--; info->xmit_tail = (info->xmit_tail + 1) & @@ -1232,7 +1232,7 @@ static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, } done: - tty_wakeup(info->tty); + tty_wakeup(info->port.tty); end: /* end of service */ cy_writeb(base_addr + (CyTIR << index), save_xir & 0x3f); @@ -1256,7 +1256,7 @@ static void cyy_chip_modem(struct cyclades_card *cinfo, int chip, mdm_change = readb(base_addr + (CyMISR << index)); mdm_status = readb(base_addr + (CyMSVR1 << index)); - if (!info->tty) + if (!info->port.tty) goto end; if (mdm_change & CyANY_DELTA) { @@ -1273,29 +1273,29 @@ static void cyy_chip_modem(struct cyclades_card *cinfo, int chip, wake_up_interruptible(&info->delta_msr_wait); } - if ((mdm_change & CyDCD) && (info->flags & ASYNC_CHECK_CD)) { + if ((mdm_change & CyDCD) && (info->port.flags & ASYNC_CHECK_CD)) { if (!(mdm_status & CyDCD)) { - tty_hangup(info->tty); - info->flags &= ~ASYNC_NORMAL_ACTIVE; + tty_hangup(info->port.tty); + info->port.flags &= ~ASYNC_NORMAL_ACTIVE; } - wake_up_interruptible(&info->open_wait); + wake_up_interruptible(&info->port.open_wait); } - if ((mdm_change & CyCTS) && (info->flags & ASYNC_CTS_FLOW)) { - if (info->tty->hw_stopped) { + if ((mdm_change & CyCTS) && (info->port.flags & ASYNC_CTS_FLOW)) { + if (info->port.tty->hw_stopped) { if (mdm_status & CyCTS) { /* cy_start isn't used because... !!! */ - info->tty->hw_stopped = 0; + info->port.tty->hw_stopped = 0; cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) | CyTxRdy); - tty_wakeup(info->tty); + tty_wakeup(info->port.tty); } } else { if (!(mdm_status & CyCTS)) { /* cy_stop isn't used because ... !!! */ - info->tty->hw_stopped = 1; + info->port.tty->hw_stopped = 1; cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) & ~CyTxRdy); @@ -1449,7 +1449,7 @@ static void cyz_handle_rx(struct cyclades_port *info, struct BUF_CTRL __iomem *buf_ctrl) { struct cyclades_card *cinfo = info->card; - struct tty_struct *tty = info->tty; + struct tty_struct *tty = info->port.tty; unsigned int char_count; int len; #ifdef BLOCKMOVE @@ -1542,7 +1542,7 @@ static void cyz_handle_tx(struct cyclades_port *info, struct BUF_CTRL __iomem *buf_ctrl) { struct cyclades_card *cinfo = info->card; - struct tty_struct *tty = info->tty; + struct tty_struct *tty = info->port.tty; u8 data; unsigned int char_count; #ifdef BLOCKMOVE @@ -1585,7 +1585,7 @@ static void cyz_handle_tx(struct cyclades_port *info, memcpy_toio((char *)(cinfo->base_addr + tx_bufaddr + tx_put), - &info->xmit_buf[info->xmit_tail], + &info->port.xmit_buf[info->xmit_tail], small_count); tx_put = (tx_put + small_count) & (tx_bufsize - 1); @@ -1597,7 +1597,7 @@ static void cyz_handle_tx(struct cyclades_port *info, } #else while (info->xmit_cnt && char_count) { - data = info->xmit_buf[info->xmit_tail]; + data = info->port.xmit_buf[info->xmit_tail]; info->xmit_cnt--; info->xmit_tail = (info->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1); @@ -1642,7 +1642,7 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) special_count = 0; delta_count = 0; info = &cinfo->ports[channel]; - tty = info->tty; + tty = info->port.tty; if (tty == NULL) continue; @@ -1668,15 +1668,15 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) case C_CM_MDCD: info->icount.dcd++; delta_count++; - if (info->flags & ASYNC_CHECK_CD) { + if (info->port.flags & ASYNC_CHECK_CD) { if ((fw_ver > 241 ? ((u_long) param) : readl(&ch_ctrl->rs_status)) & C_RS_DCD) { - wake_up_interruptible(&info->open_wait); + wake_up_interruptible(&info->port.open_wait); } else { - tty_hangup(info->tty); - wake_up_interruptible(&info->open_wait); - info->flags &= ~ASYNC_NORMAL_ACTIVE; + tty_hangup(info->port.tty); + wake_up_interruptible(&info->port.open_wait); + info->port.flags &= ~ASYNC_NORMAL_ACTIVE; } } break; @@ -1814,7 +1814,7 @@ static void cyz_poll(unsigned long arg) for (port = 0; port < cinfo->nports; port++) { info = &cinfo->ports[port]; - tty = info->tty; + tty = info->port.tty; buf_ctrl = &(zfw_ctrl->buf_ctrl[port]); if (!info->throttle) @@ -1853,22 +1853,22 @@ static int startup(struct cyclades_port *info) spin_lock_irqsave(&card->card_lock, flags); - if (info->flags & ASYNC_INITIALIZED) { + if (info->port.flags & ASYNC_INITIALIZED) { free_page(page); goto errout; } if (!info->type) { - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); + if (info->port.tty) + set_bit(TTY_IO_ERROR, &info->port.tty->flags); free_page(page); goto errout; } - if (info->xmit_buf) + if (info->port.xmit_buf) free_page(page); else - info->xmit_buf = (unsigned char *)page; + info->port.xmit_buf = (unsigned char *)page; spin_unlock_irqrestore(&card->card_lock, flags); @@ -1909,10 +1909,10 @@ static int startup(struct cyclades_port *info) cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) | CyRxData); - info->flags |= ASYNC_INITIALIZED; + info->port.flags |= ASYNC_INITIALIZED; - if (info->tty) - clear_bit(TTY_IO_ERROR, &info->tty->flags); + if (info->port.tty) + clear_bit(TTY_IO_ERROR, &info->port.tty->flags); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; info->breakon = info->breakoff = 0; memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats)); @@ -1994,9 +1994,9 @@ static int startup(struct cyclades_port *info) /* enable send, recv, modem !!! */ - info->flags |= ASYNC_INITIALIZED; - if (info->tty) - clear_bit(TTY_IO_ERROR, &info->tty->flags); + info->port.flags |= ASYNC_INITIALIZED; + if (info->port.tty) + clear_bit(TTY_IO_ERROR, &info->port.tty->flags); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; info->breakon = info->breakoff = 0; memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats)); @@ -2065,7 +2065,7 @@ static void shutdown(struct cyclades_port *info) void __iomem *base_addr; int chip, channel, index; - if (!(info->flags & ASYNC_INITIALIZED)) + if (!(info->port.flags & ASYNC_INITIALIZED)) return; card = info->card; @@ -2087,14 +2087,14 @@ static void shutdown(struct cyclades_port *info) /* Clear delta_msr_wait queue to avoid mem leaks. */ wake_up_interruptible(&info->delta_msr_wait); - if (info->xmit_buf) { + if (info->port.xmit_buf) { unsigned char *temp; - temp = info->xmit_buf; - info->xmit_buf = NULL; + temp = info->port.xmit_buf; + info->port.xmit_buf = NULL; free_page((unsigned long)temp); } cy_writeb(base_addr + (CyCAR << index), (u_char) channel); - if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { + if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL)) { cy_writeb(base_addr + (CyMSVR1 << index), ~CyRTS); cy_writeb(base_addr + (CyMSVR2 << index), ~CyDTR); #ifdef CY_DEBUG_DTR @@ -2108,9 +2108,9 @@ static void shutdown(struct cyclades_port *info) /* it may be appropriate to clear _XMIT at some later date (after testing)!!! */ - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); - info->flags &= ~ASYNC_INITIALIZED; + if (info->port.tty) + set_bit(TTY_IO_ERROR, &info->port.tty->flags); + info->port.flags &= ~ASYNC_INITIALIZED; spin_unlock_irqrestore(&card->card_lock, flags); } else { struct FIRM_ID __iomem *firm_id; @@ -2136,14 +2136,14 @@ static void shutdown(struct cyclades_port *info) spin_lock_irqsave(&card->card_lock, flags); - if (info->xmit_buf) { + if (info->port.xmit_buf) { unsigned char *temp; - temp = info->xmit_buf; - info->xmit_buf = NULL; + temp = info->port.xmit_buf; + info->port.xmit_buf = NULL; free_page((unsigned long)temp); } - if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { + if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL)) { cy_writel(&ch_ctrl[channel].rs_control, (__u32)(readl(&ch_ctrl[channel].rs_control) & ~(C_RS_RTS | C_RS_DTR))); @@ -2158,9 +2158,9 @@ static void shutdown(struct cyclades_port *info) #endif } - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); - info->flags &= ~ASYNC_INITIALIZED; + if (info->port.tty) + set_bit(TTY_IO_ERROR, &info->port.tty->flags); + info->port.flags &= ~ASYNC_INITIALIZED; spin_unlock_irqrestore(&card->card_lock, flags); } @@ -2194,10 +2194,10 @@ block_til_ready(struct tty_struct *tty, struct file *filp, * If the device is in the middle of being closed, then block * until it's done, and then try again. */ - if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { - wait_event_interruptible(info->close_wait, - !(info->flags & ASYNC_CLOSING)); - return (info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS; + if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) { + wait_event_interruptible(info->port.close_wait, + !(info->port.flags & ASYNC_CLOSING)); + return (info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS; } /* @@ -2206,32 +2206,32 @@ block_til_ready(struct tty_struct *tty, struct file *filp, */ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { - info->flags |= ASYNC_NORMAL_ACTIVE; + info->port.flags |= ASYNC_NORMAL_ACTIVE; return 0; } /* * Block waiting for the carrier detect and the line to become * free (i.e., not in use by the callout). While we are in - * this loop, info->count is dropped by one, so that + * this loop, info->port.count is dropped by one, so that * cy_close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ retval = 0; - add_wait_queue(&info->open_wait, &wait); + add_wait_queue(&info->port.open_wait, &wait); #ifdef CY_DEBUG_OPEN printk(KERN_DEBUG "cyc block_til_ready before block: ttyC%d, " - "count = %d\n", info->line, info->count); + "count = %d\n", info->line, info->port.count); #endif spin_lock_irqsave(&cinfo->card_lock, flags); if (!tty_hung_up_p(filp)) - info->count--; + info->port.count--; spin_unlock_irqrestore(&cinfo->card_lock, flags); #ifdef CY_DEBUG_COUNT printk(KERN_DEBUG "cyc block_til_ready: (%d): decrementing count to " - "%d\n", current->pid, info->count); + "%d\n", current->pid, info->port.count); #endif - info->blocked_open++; + info->port.blocked_open++; if (!IS_CYC_Z(*cinfo)) { chip = channel >> 2; @@ -2260,8 +2260,8 @@ block_til_ready(struct tty_struct *tty, struct file *filp, set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || - !(info->flags & ASYNC_INITIALIZED)) { - retval = ((info->flags & ASYNC_HUP_NOTIFY) ? + !(info->port.flags & ASYNC_INITIALIZED)) { + retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); break; } @@ -2269,7 +2269,7 @@ block_til_ready(struct tty_struct *tty, struct file *filp, spin_lock_irqsave(&cinfo->card_lock, flags); cy_writeb(base_addr + (CyCAR << index), (u_char) channel); - if (!(info->flags & ASYNC_CLOSING) && (C_CLOCAL(tty) || + if (!(info->port.flags & ASYNC_CLOSING) && (C_CLOCAL(tty) || (readb(base_addr + (CyMSVR1 << index)) & CyDCD))) { spin_unlock_irqrestore(&cinfo->card_lock, flags); @@ -2284,7 +2284,7 @@ block_til_ready(struct tty_struct *tty, struct file *filp, #ifdef CY_DEBUG_OPEN printk(KERN_DEBUG "cyc block_til_ready blocking: " "ttyC%d, count = %d\n", - info->line, info->count); + info->line, info->port.count); #endif schedule(); } @@ -2298,7 +2298,7 @@ block_til_ready(struct tty_struct *tty, struct file *filp, firm_id = base_addr + ID_ADDRESS; if (!ISZLOADED(*cinfo)) { __set_current_state(TASK_RUNNING); - remove_wait_queue(&info->open_wait, &wait); + remove_wait_queue(&info->port.open_wait, &wait); return -EINVAL; } @@ -2327,12 +2327,12 @@ block_til_ready(struct tty_struct *tty, struct file *filp, set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || - !(info->flags & ASYNC_INITIALIZED)) { - retval = ((info->flags & ASYNC_HUP_NOTIFY) ? + !(info->port.flags & ASYNC_INITIALIZED)) { + retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); break; } - if (!(info->flags & ASYNC_CLOSING) && (C_CLOCAL(tty) || + if (!(info->port.flags & ASYNC_CLOSING) && (C_CLOCAL(tty) || (readl(&ch_ctrl[channel].rs_status) & C_RS_DCD))) { break; @@ -2344,28 +2344,28 @@ block_til_ready(struct tty_struct *tty, struct file *filp, #ifdef CY_DEBUG_OPEN printk(KERN_DEBUG "cyc block_til_ready blocking: " "ttyC%d, count = %d\n", - info->line, info->count); + info->line, info->port.count); #endif schedule(); } } __set_current_state(TASK_RUNNING); - remove_wait_queue(&info->open_wait, &wait); + remove_wait_queue(&info->port.open_wait, &wait); if (!tty_hung_up_p(filp)) { - info->count++; + info->port.count++; #ifdef CY_DEBUG_COUNT printk(KERN_DEBUG "cyc:block_til_ready (%d): incrementing " - "count to %d\n", current->pid, info->count); + "count to %d\n", current->pid, info->port.count); #endif } - info->blocked_open--; + info->port.blocked_open--; #ifdef CY_DEBUG_OPEN printk(KERN_DEBUG "cyc:block_til_ready after blocking: ttyC%d, " - "count = %d\n", info->line, info->count); + "count = %d\n", info->line, info->port.count); #endif if (retval) return retval; - info->flags |= ASYNC_NORMAL_ACTIVE; + info->port.flags |= ASYNC_NORMAL_ACTIVE; return 0; } /* block_til_ready */ @@ -2456,27 +2456,27 @@ static int cy_open(struct tty_struct *tty, struct file *filp) printk(KERN_DEBUG "cyc:cy_open ttyC%d\n", info->line); #endif tty->driver_data = info; - info->tty = tty; + info->port.tty = tty; if (serial_paranoia_check(info, tty->name, "cy_open")) return -ENODEV; #ifdef CY_DEBUG_OPEN printk(KERN_DEBUG "cyc:cy_open ttyC%d, count = %d\n", info->line, - info->count); + info->port.count); #endif - info->count++; + info->port.count++; #ifdef CY_DEBUG_COUNT printk(KERN_DEBUG "cyc:cy_open (%d): incrementing count to %d\n", - current->pid, info->count); + current->pid, info->port.count); #endif /* * If the port is the middle of closing, bail out now */ - if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { - wait_event_interruptible(info->close_wait, - !(info->flags & ASYNC_CLOSING)); - return (info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS; + if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) { + wait_event_interruptible(info->port.close_wait, + !(info->port.flags & ASYNC_CLOSING)); + return (info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS; } /* @@ -2641,9 +2641,9 @@ static void cy_close(struct tty_struct *tty, struct file *filp) } #ifdef CY_DEBUG_OPEN printk(KERN_DEBUG "cyc:cy_close ttyC%d, count = %d\n", info->line, - info->count); + info->port.count); #endif - if ((tty->count == 1) && (info->count != 1)) { + if ((tty->count == 1) && (info->port.count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->count should always @@ -2652,24 +2652,24 @@ static void cy_close(struct tty_struct *tty, struct file *filp) * serial port won't be shutdown. */ printk(KERN_ERR "cyc:cy_close: bad serial port count; " - "tty->count is 1, info->count is %d\n", info->count); - info->count = 1; + "tty->count is 1, info->port.count is %d\n", info->port.count); + info->port.count = 1; } #ifdef CY_DEBUG_COUNT printk(KERN_DEBUG "cyc:cy_close at (%d): decrementing count to %d\n", - current->pid, info->count - 1); + current->pid, info->port.count - 1); #endif - if (--info->count < 0) { + if (--info->port.count < 0) { #ifdef CY_DEBUG_COUNT printk(KERN_DEBUG "cyc:cyc_close setting count to 0\n"); #endif - info->count = 0; + info->port.count = 0; } - if (info->count) { + if (info->port.count) { spin_unlock_irqrestore(&card->card_lock, flags); return; } - info->flags |= ASYNC_CLOSING; + info->port.flags |= ASYNC_CLOSING; /* * Now we wait for the transmit buffer to clear; and we notify @@ -2692,7 +2692,7 @@ static void cy_close(struct tty_struct *tty, struct file *filp) cy_writeb(base_addr + (CyCAR << index), (u_char) channel); cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) & ~CyRxData); - if (info->flags & ASYNC_INITIALIZED) { + if (info->port.flags & ASYNC_INITIALIZED) { /* Waiting for on-board buffers to be empty before closing the port */ spin_unlock_irqrestore(&card->card_lock, flags); @@ -2731,18 +2731,18 @@ static void cy_close(struct tty_struct *tty, struct file *filp) spin_lock_irqsave(&card->card_lock, flags); tty->closing = 0; - info->tty = NULL; - if (info->blocked_open) { + info->port.tty = NULL; + if (info->port.blocked_open) { spin_unlock_irqrestore(&card->card_lock, flags); if (info->close_delay) { msleep_interruptible(jiffies_to_msecs (info->close_delay)); } - wake_up_interruptible(&info->open_wait); + wake_up_interruptible(&info->port.open_wait); spin_lock_irqsave(&card->card_lock, flags); } - info->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); - wake_up_interruptible(&info->close_wait); + info->port.flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); + wake_up_interruptible(&info->port.close_wait); #ifdef CY_DEBUG_OTHER printk(KERN_DEBUG "cyc:cy_close done\n"); @@ -2777,7 +2777,7 @@ static int cy_write(struct tty_struct *tty, const unsigned char *buf, int count) if (serial_paranoia_check(info, tty->name, "cy_write")) return 0; - if (!info->xmit_buf) + if (!info->port.xmit_buf) return 0; spin_lock_irqsave(&info->card->card_lock, flags); @@ -2788,7 +2788,7 @@ static int cy_write(struct tty_struct *tty, const unsigned char *buf, int count) if (c <= 0) break; - memcpy(info->xmit_buf + info->xmit_head, buf, c); + memcpy(info->port.xmit_buf + info->xmit_head, buf, c); info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE - 1); info->xmit_cnt += c; @@ -2826,7 +2826,7 @@ static int cy_put_char(struct tty_struct *tty, unsigned char ch) if (serial_paranoia_check(info, tty->name, "cy_put_char")) return 0; - if (!info->xmit_buf) + if (!info->port.xmit_buf) return 0; spin_lock_irqsave(&info->card->card_lock, flags); @@ -2835,7 +2835,7 @@ static int cy_put_char(struct tty_struct *tty, unsigned char ch) return 0; } - info->xmit_buf[info->xmit_head++] = ch; + info->port.xmit_buf[info->xmit_head++] = ch; info->xmit_head &= SERIAL_XMIT_SIZE - 1; info->xmit_cnt++; info->idle_stats.xmit_bytes++; @@ -2860,7 +2860,7 @@ static void cy_flush_chars(struct tty_struct *tty) return; if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || - !info->xmit_buf) + !info->port.xmit_buf) return; start_xmit(info); @@ -2988,27 +2988,27 @@ static void set_line_char(struct cyclades_port *info) int baud, baud_rate = 0; int i; - if (!info->tty || !info->tty->termios) + if (!info->port.tty || !info->port.tty->termios) return; if (info->line == -1) return; - cflag = info->tty->termios->c_cflag; - iflag = info->tty->termios->c_iflag; + cflag = info->port.tty->termios->c_cflag; + iflag = info->port.tty->termios->c_iflag; /* * Set up the tty->alt_speed kludge */ - if (info->tty) { - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - info->tty->alt_speed = 57600; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - info->tty->alt_speed = 115200; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - info->tty->alt_speed = 230400; - if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - info->tty->alt_speed = 460800; + if (info->port.tty) { + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->port.tty->alt_speed = 57600; + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->port.tty->alt_speed = 115200; + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->port.tty->alt_speed = 230400; + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->port.tty->alt_speed = 460800; } card = info->card; @@ -3020,8 +3020,8 @@ static void set_line_char(struct cyclades_port *info) index = card->bus_index; /* baud rate */ - baud = tty_get_baud_rate(info->tty); - if (baud == 38400 && (info->flags & ASYNC_SPD_MASK) == + baud = tty_get_baud_rate(info->port.tty); + if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) { if (info->custom_divisor) baud_rate = info->baud / info->custom_divisor; @@ -3038,7 +3038,7 @@ static void set_line_char(struct cyclades_port *info) if (i == 20) i = 19; /* CD1400_MAX_SPEED */ - if (baud == 38400 && (info->flags & ASYNC_SPD_MASK) == + if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) { cyy_baud_calc(info, baud_rate); } else { @@ -3059,7 +3059,7 @@ static void set_line_char(struct cyclades_port *info) /* get it right for 134.5 baud */ info->timeout = (info->xmit_fifo_size * HZ * 30 / 269) + 2; - } else if (baud == 38400 && (info->flags & ASYNC_SPD_MASK) == + } else if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) { info->timeout = (info->xmit_fifo_size * HZ * 15 / baud_rate) + 2; @@ -3108,16 +3108,16 @@ static void set_line_char(struct cyclades_port *info) /* CTS flow control flag */ if (cflag & CRTSCTS) { - info->flags |= ASYNC_CTS_FLOW; + info->port.flags |= ASYNC_CTS_FLOW; info->cor2 |= CyCtsAE; } else { - info->flags &= ~ASYNC_CTS_FLOW; + info->port.flags &= ~ASYNC_CTS_FLOW; info->cor2 &= ~CyCtsAE; } if (cflag & CLOCAL) - info->flags &= ~ASYNC_CHECK_CD; + info->port.flags &= ~ASYNC_CHECK_CD; else - info->flags |= ASYNC_CHECK_CD; + info->port.flags |= ASYNC_CHECK_CD; /*********************************************** The hardware option, CyRtsAO, presents RTS when @@ -3146,8 +3146,8 @@ static void set_line_char(struct cyclades_port *info) /* set line characteristics according configuration */ cy_writeb(base_addr + (CySCHR1 << index), - START_CHAR(info->tty)); - cy_writeb(base_addr + (CySCHR2 << index), STOP_CHAR(info->tty)); + START_CHAR(info->port.tty)); + cy_writeb(base_addr + (CySCHR2 << index), STOP_CHAR(info->port.tty)); cy_writeb(base_addr + (CyCOR1 << index), info->cor1); cy_writeb(base_addr + (CyCOR2 << index), info->cor2); cy_writeb(base_addr + (CyCOR3 << index), info->cor3); @@ -3163,7 +3163,7 @@ static void set_line_char(struct cyclades_port *info) (info->default_timeout ? info->default_timeout : 0x02)); /* 10ms rx timeout */ - if (C_CLOCAL(info->tty)) { + if (C_CLOCAL(info->port.tty)) { /* without modem intr */ cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) | CyMdmCh); @@ -3226,8 +3226,8 @@ static void set_line_char(struct cyclades_port *info) #endif } - if (info->tty) - clear_bit(TTY_IO_ERROR, &info->tty->flags); + if (info->port.tty) + clear_bit(TTY_IO_ERROR, &info->port.tty->flags); spin_unlock_irqrestore(&card->card_lock, flags); } else { @@ -3250,8 +3250,8 @@ static void set_line_char(struct cyclades_port *info) buf_ctrl = &zfw_ctrl->buf_ctrl[channel]; /* baud rate */ - baud = tty_get_baud_rate(info->tty); - if (baud == 38400 && (info->flags & ASYNC_SPD_MASK) == + baud = tty_get_baud_rate(info->port.tty); + if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) { if (info->custom_divisor) baud_rate = info->baud / info->custom_divisor; @@ -3266,7 +3266,7 @@ static void set_line_char(struct cyclades_port *info) /* get it right for 134.5 baud */ info->timeout = (info->xmit_fifo_size * HZ * 30 / 269) + 2; - } else if (baud == 38400 && (info->flags & ASYNC_SPD_MASK) == + } else if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) { info->timeout = (info->xmit_fifo_size * HZ * 15 / baud_rate) + 2; @@ -3318,7 +3318,7 @@ static void set_line_char(struct cyclades_port *info) } /* As the HW flow control is done in firmware, the driver doesn't need to care about it */ - info->flags &= ~ASYNC_CTS_FLOW; + info->port.flags &= ~ASYNC_CTS_FLOW; /* XON/XOFF/XANY flow control flags */ sw_flow = 0; @@ -3337,9 +3337,9 @@ static void set_line_char(struct cyclades_port *info) /* CD sensitivity */ if (cflag & CLOCAL) - info->flags &= ~ASYNC_CHECK_CD; + info->port.flags &= ~ASYNC_CHECK_CD; else - info->flags |= ASYNC_CHECK_CD; + info->port.flags |= ASYNC_CHECK_CD; if (baud == 0) { /* baud rate is zero, turn off line */ cy_writel(&ch_ctrl->rs_control, @@ -3361,8 +3361,8 @@ static void set_line_char(struct cyclades_port *info) "was %x\n", info->line, retval); } - if (info->tty) - clear_bit(TTY_IO_ERROR, &info->tty->flags); + if (info->port.tty) + clear_bit(TTY_IO_ERROR, &info->port.tty->flags); } } /* set_line_char */ @@ -3381,7 +3381,7 @@ get_serial_info(struct cyclades_port *info, tmp.port = (info->card - cy_card) * 0x100 + info->line - cinfo->first_line; tmp.irq = cinfo->irq; - tmp.flags = info->flags; + tmp.flags = info->port.flags; tmp.close_delay = info->close_delay; tmp.closing_wait = info->closing_wait; tmp.baud_base = info->baud; @@ -3406,9 +3406,9 @@ set_serial_info(struct cyclades_port *info, new_serial.baud_base != info->baud || (new_serial.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK) != - (info->flags & ASYNC_FLAGS & ~ASYNC_USR_MASK)) + (info->port.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK)) return -EPERM; - info->flags = (info->flags & ~ASYNC_USR_MASK) | + info->port.flags = (info->port.flags & ~ASYNC_USR_MASK) | (new_serial.flags & ASYNC_USR_MASK); info->baud = new_serial.baud_base; info->custom_divisor = new_serial.custom_divisor; @@ -3422,13 +3422,13 @@ set_serial_info(struct cyclades_port *info, info->baud = new_serial.baud_base; info->custom_divisor = new_serial.custom_divisor; - info->flags = (info->flags & ~ASYNC_FLAGS) | + info->port.flags = (info->port.flags & ~ASYNC_FLAGS) | (new_serial.flags & ASYNC_FLAGS); info->close_delay = new_serial.close_delay * HZ / 100; info->closing_wait = new_serial.closing_wait * HZ / 100; check_and_exit: - if (info->flags & ASYNC_INITIALIZED) { + if (info->port.flags & ASYNC_INITIALIZED) { set_line_char(info); return 0; } else { @@ -4097,7 +4097,7 @@ static void cy_set_termios(struct tty_struct *tty, struct ktermios *old_termios) */ if (!(old_termios->c_cflag & CLOCAL) && (tty->termios->c_cflag & CLOCAL)) - wake_up_interruptible(&info->open_wait); + wake_up_interruptible(&info->port.open_wait); #endif } /* cy_set_termios */ @@ -4326,14 +4326,14 @@ static void cy_hangup(struct tty_struct *tty) cy_flush_buffer(tty); shutdown(info); - info->count = 0; + info->port.count = 0; #ifdef CY_DEBUG_COUNT printk(KERN_DEBUG "cyc:cy_hangup (%d): setting count to 0\n", current->pid); #endif - info->tty = NULL; - info->flags &= ~ASYNC_NORMAL_ACTIVE; - wake_up_interruptible(&info->open_wait); + info->port.tty = NULL; + info->port.flags &= ~ASYNC_NORMAL_ACTIVE; + wake_up_interruptible(&info->port.open_wait); } /* cy_hangup */ /* @@ -4379,12 +4379,11 @@ static int __devinit cy_init_card(struct cyclades_card *cinfo) info->magic = CYCLADES_MAGIC; info->card = cinfo; info->line = port; - info->flags = STD_COM_FLAGS; info->closing_wait = CLOSING_WAIT_DELAY; info->close_delay = 5 * HZ / 10; - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->close_wait); + tty_port_init(&info->port); + info->port.flags = STD_COM_FLAGS; init_completion(&info->shutdown_wait); init_waitqueue_head(&info->delta_msr_wait); @@ -5237,7 +5236,7 @@ cyclades_get_proc_info(char *buf, char **start, off_t offset, int length, for (j = 0; j < cy_card[i].nports; j++) { info = &cy_card[i].ports[j]; - if (info->count) + if (info->port.count) size = sprintf(buf + len, "%3d %8lu %10lu %8lu " "%10lu %8lu %9lu %6ld\n", info->line, (cur_jifs - info->idle_stats.in_use) / @@ -5247,7 +5246,7 @@ cyclades_get_proc_info(char *buf, char **start, off_t offset, int length, (cur_jifs - info->idle_stats.recv_idle)/ HZ, info->idle_stats.overruns, /* FIXME: double check locking */ - (long)info->tty->ldisc.ops->num); + (long)info->port.tty->ldisc.ops->num); else size = sprintf(buf + len, "%3d %8lu %10lu %8lu " "%10lu %8lu %9lu %6ld\n", diff --git a/include/linux/cyclades.h b/include/linux/cyclades.h index 504cb2c3fa9a..a982b74a6ee6 100644 --- a/include/linux/cyclades.h +++ b/include/linux/cyclades.h @@ -550,11 +550,11 @@ struct cyclades_icount { struct cyclades_port { int magic; + struct tty_port port; struct cyclades_card *card; int line; int flags; /* defined in tty.h */ int type; /* UART type */ - struct tty_struct *tty; int read_status_mask; int ignore_status_mask; int timeout; @@ -569,11 +569,8 @@ struct cyclades_port { u8 x_char; /* to be pushed out ASAP */ int close_delay; unsigned short closing_wait; - int count; /* # of fd on device */ int breakon; int breakoff; - int blocked_open; /* # of blocked opens */ - unsigned char *xmit_buf; int xmit_head; int xmit_tail; int xmit_cnt; @@ -583,8 +580,6 @@ struct cyclades_port { struct cyclades_monitor mon; struct cyclades_idle_stats idle_stats; struct cyclades_icount icount; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; struct completion shutdown_wait; wait_queue_head_t delta_msr_wait; int throttle; -- cgit v1.2.3 From 44b7d1b37f786c61d0e382b6f72f605f73de284b Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 16 Jul 2008 21:57:18 +0100 Subject: tty: add more tty_port fields Move more bits into the tty_port structure Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/cyclades.c | 28 ++++---- drivers/char/isicom.c | 28 ++++---- drivers/char/moxa.c | 8 +-- drivers/char/mxser.c | 29 ++++---- drivers/char/riscom8.c | 24 ++++--- drivers/char/riscom8.h | 2 - drivers/char/rocket.c | 23 +++---- drivers/char/rocket.h | 4 +- drivers/char/rocket_int.h | 2 - drivers/char/specialix.c | 153 +++++++++++++++++++++---------------------- drivers/char/specialix_io8.h | 8 +-- drivers/char/sx.c | 2 +- drivers/char/synclink.c | 16 ++--- drivers/char/synclink_gt.c | 16 ++--- drivers/char/synclinkmp.c | 14 ++-- drivers/char/tty_io.c | 2 + include/linux/cyclades.h | 6 +- include/linux/tty.h | 2 + 18 files changed, 169 insertions(+), 198 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 0144b19d1036..e991dc85f2fb 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -2677,8 +2677,8 @@ static void cy_close(struct tty_struct *tty, struct file *filp) */ tty->closing = 1; spin_unlock_irqrestore(&card->card_lock, flags); - if (info->closing_wait != CY_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, info->closing_wait); + if (info->port.closing_wait != CY_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->port.closing_wait); spin_lock_irqsave(&card->card_lock, flags); @@ -2734,9 +2734,9 @@ static void cy_close(struct tty_struct *tty, struct file *filp) info->port.tty = NULL; if (info->port.blocked_open) { spin_unlock_irqrestore(&card->card_lock, flags); - if (info->close_delay) { + if (info->port.close_delay) { msleep_interruptible(jiffies_to_msecs - (info->close_delay)); + (info->port.close_delay)); } wake_up_interruptible(&info->port.open_wait); spin_lock_irqsave(&card->card_lock, flags); @@ -3382,8 +3382,8 @@ get_serial_info(struct cyclades_port *info, cinfo->first_line; tmp.irq = cinfo->irq; tmp.flags = info->port.flags; - tmp.close_delay = info->close_delay; - tmp.closing_wait = info->closing_wait; + tmp.close_delay = info->port.close_delay; + tmp.closing_wait = info->port.closing_wait; tmp.baud_base = info->baud; tmp.custom_divisor = info->custom_divisor; tmp.hub6 = 0; /*!!! */ @@ -3402,7 +3402,7 @@ set_serial_info(struct cyclades_port *info, old_info = *info; if (!capable(CAP_SYS_ADMIN)) { - if (new_serial.close_delay != info->close_delay || + if (new_serial.close_delay != info->port.close_delay || new_serial.baud_base != info->baud || (new_serial.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK) != @@ -3424,8 +3424,8 @@ set_serial_info(struct cyclades_port *info, info->custom_divisor = new_serial.custom_divisor; info->port.flags = (info->port.flags & ~ASYNC_FLAGS) | (new_serial.flags & ASYNC_FLAGS); - info->close_delay = new_serial.close_delay * HZ / 100; - info->closing_wait = new_serial.closing_wait * HZ / 100; + info->port.close_delay = new_serial.close_delay * HZ / 100; + info->port.closing_wait = new_serial.closing_wait * HZ / 100; check_and_exit: if (info->port.flags & ASYNC_INITIALIZED) { @@ -3971,11 +3971,11 @@ cy_ioctl(struct tty_struct *tty, struct file *file, break; #endif /* CONFIG_CYZ_INTR */ case CYSETWAIT: - info->closing_wait = (unsigned short)arg * HZ / 100; + info->port.closing_wait = (unsigned short)arg * HZ / 100; ret_val = 0; break; case CYGETWAIT: - ret_val = info->closing_wait / (HZ / 100); + ret_val = info->port.closing_wait / (HZ / 100); break; case TIOCGSERIAL: ret_val = get_serial_info(info, argp); @@ -4376,13 +4376,13 @@ static int __devinit cy_init_card(struct cyclades_card *cinfo) for (port = cinfo->first_line; port < cinfo->first_line + nports; port++) { info = &cinfo->ports[port - cinfo->first_line]; + tty_port_init(&info->port); info->magic = CYCLADES_MAGIC; info->card = cinfo; info->line = port; - info->closing_wait = CLOSING_WAIT_DELAY; - info->close_delay = 5 * HZ / 10; - tty_port_init(&info->port); + info->port.closing_wait = CLOSING_WAIT_DELAY; + info->port.close_delay = 5 * HZ / 10; info->port.flags = STD_COM_FLAGS; init_completion(&info->shutdown_wait); init_waitqueue_head(&info->delta_msr_wait); diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index 5a53c15b0dc2..d4281df10c22 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -199,10 +199,8 @@ struct isi_board { struct isi_port { unsigned short magic; struct tty_port port; - int close_delay; u16 channel; u16 status; - u16 closing_wait; struct isi_board *card; unsigned char *xmit_buf; int xmit_head; @@ -1051,8 +1049,8 @@ static void isicom_close(struct tty_struct *tty, struct file *filp) tty->closing = 1; spin_unlock_irqrestore(&card->card_lock, flags); - if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, port->closing_wait); + if (port->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, port->port.closing_wait); /* indicate to the card that no more data can be received on this port */ spin_lock_irqsave(&card->card_lock, flags); @@ -1071,10 +1069,10 @@ static void isicom_close(struct tty_struct *tty, struct file *filp) if (port->port.blocked_open) { spin_unlock_irqrestore(&card->card_lock, flags); - if (port->close_delay) { + if (port->port.close_delay) { pr_dbg("scheduling until time out.\n"); msleep_interruptible( - jiffies_to_msecs(port->close_delay)); + jiffies_to_msecs(port->port.close_delay)); } spin_lock_irqsave(&card->card_lock, flags); wake_up_interruptible(&port->port.open_wait); @@ -1256,8 +1254,8 @@ static int isicom_set_serial_info(struct isi_port *port, (newinfo.flags & ASYNC_SPD_MASK)); if (!capable(CAP_SYS_ADMIN)) { - if ((newinfo.close_delay != port->close_delay) || - (newinfo.closing_wait != port->closing_wait) || + if ((newinfo.close_delay != port->port.close_delay) || + (newinfo.closing_wait != port->port.closing_wait) || ((newinfo.flags & ~ASYNC_USR_MASK) != (port->port.flags & ~ASYNC_USR_MASK))) { unlock_kernel(); @@ -1266,8 +1264,8 @@ static int isicom_set_serial_info(struct isi_port *port, port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) | (newinfo.flags & ASYNC_USR_MASK)); } else { - port->close_delay = newinfo.close_delay; - port->closing_wait = newinfo.closing_wait; + port->port.close_delay = newinfo.close_delay; + port->port.closing_wait = newinfo.closing_wait; port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) | (newinfo.flags & ASYNC_FLAGS)); } @@ -1294,8 +1292,8 @@ static int isicom_get_serial_info(struct isi_port *port, out_info.irq = port->card->irq; out_info.flags = port->port.flags; /* out_info.baud_base = ? */ - out_info.close_delay = port->close_delay; - out_info.closing_wait = port->closing_wait; + out_info.close_delay = port->port.close_delay; + out_info.closing_wait = port->port.closing_wait; unlock_kernel(); if (copy_to_user(info, &out_info, sizeof(out_info))) return -EFAULT; @@ -1804,13 +1802,13 @@ static int __init isicom_init(void) isi_card[idx].ports = port; spin_lock_init(&isi_card[idx].card_lock); for (channel = 0; channel < 16; channel++, port++) { + tty_port_init(&port->port); port->magic = ISICOM_MAGIC; port->card = &isi_card[idx]; port->channel = channel; - port->close_delay = 50 * HZ/100; - port->closing_wait = 3000 * HZ/100; + port->port.close_delay = 50 * HZ/100; + port->port.closing_wait = 3000 * HZ/100; port->status = 0; - tty_port_init(&port->port); /* . . . */ } isi_card[idx].base = 0; diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c index dd10f143d96f..2bba250ffc8e 100644 --- a/drivers/char/moxa.c +++ b/drivers/char/moxa.c @@ -135,7 +135,6 @@ struct moxa_port { void __iomem *tableAddr; int type; - int close_delay; int cflag; unsigned long statusflags; @@ -822,10 +821,9 @@ static int moxa_init_board(struct moxa_board_conf *brd, struct device *dev) } for (i = 0, p = brd->ports; i < MAX_PORTS_PER_BOARD; i++, p++) { + tty_port_init(&p->port); p->type = PORT_16550A; - p->close_delay = 5 * HZ / 10; p->cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL; - tty_port_init(&p->port); } switch (brd->boardType) { @@ -2124,7 +2122,7 @@ static int moxa_get_serial_info(struct moxa_port *info, .line = info->port.tty->index, .flags = info->port.flags, .baud_base = 921600, - .close_delay = info->close_delay + .close_delay = info->port.close_delay }; return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0; } @@ -2148,7 +2146,7 @@ static int moxa_set_serial_info(struct moxa_port *info, (info->port.flags & ~ASYNC_USR_MASK))) return -EPERM; } else - info->close_delay = new_serial.close_delay * HZ / 100; + info->port.close_delay = new_serial.close_delay * HZ / 100; new_serial.flags = (new_serial.flags & ~ASYNC_FLAGS); new_serial.flags |= (info->port.flags & ASYNC_FLAGS); diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index e83ccee03161..6307e301bd26 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -243,10 +243,7 @@ struct mxser_port { unsigned char ldisc_stop_rx; int custom_divisor; - int close_delay; - unsigned short closing_wait; unsigned char err_shadow; - unsigned long event; struct async_icount icount; /* kernel counters for 4 input interrupts */ int timeout; @@ -1199,8 +1196,8 @@ static void mxser_close(struct tty_struct *tty, struct file *filp) * the line discipline to only process XON/XOFF characters. */ tty->closing = 1; - if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, info->closing_wait); + if (info->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->port.closing_wait); /* * At this point we stop accepting input. To do this, we * disable the receive line status interrupts, and tell the @@ -1231,11 +1228,10 @@ static void mxser_close(struct tty_struct *tty, struct file *filp) tty_ldisc_flush(tty); tty->closing = 0; - info->event = 0; info->port.tty = NULL; if (info->port.blocked_open) { - if (info->close_delay) - schedule_timeout_interruptible(info->close_delay); + if (info->port.close_delay) + schedule_timeout_interruptible(info->port.close_delay); wake_up_interruptible(&info->port.open_wait); } @@ -1370,8 +1366,8 @@ static int mxser_get_serial_info(struct mxser_port *info, .irq = info->board->irq, .flags = info->port.flags, .baud_base = info->baud_base, - .close_delay = info->close_delay, - .closing_wait = info->closing_wait, + .close_delay = info->port.close_delay, + .closing_wait = info->port.closing_wait, .custom_divisor = info->custom_divisor, .hub6 = 0 }; @@ -1402,7 +1398,7 @@ static int mxser_set_serial_info(struct mxser_port *info, if (!capable(CAP_SYS_ADMIN)) { if ((new_serial.baud_base != info->baud_base) || - (new_serial.close_delay != info->close_delay) || + (new_serial.close_delay != info->port.close_delay) || ((new_serial.flags & ~ASYNC_USR_MASK) != (info->port.flags & ~ASYNC_USR_MASK))) return -EPERM; info->port.flags = ((info->port.flags & ~ASYNC_USR_MASK) | @@ -1414,8 +1410,8 @@ static int mxser_set_serial_info(struct mxser_port *info, */ info->port.flags = ((info->port.flags & ~ASYNC_FLAGS) | (new_serial.flags & ASYNC_FLAGS)); - info->close_delay = new_serial.close_delay * HZ / 100; - info->closing_wait = new_serial.closing_wait * HZ / 100; + info->port.close_delay = new_serial.close_delay * HZ / 100; + info->port.closing_wait = new_serial.closing_wait * HZ / 100; info->port.tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; info->port.tty->low_latency = 0; @@ -2214,7 +2210,6 @@ static void mxser_hangup(struct tty_struct *tty) mxser_flush_buffer(tty); mxser_shutdown(info); - info->event = 0; info->port.count = 0; info->port.flags &= ~ASYNC_NORMAL_ACTIVE; info->port.tty = NULL; @@ -2545,6 +2540,7 @@ static int __devinit mxser_initbrd(struct mxser_board *brd, for (i = 0; i < brd->info->nports; i++) { info = &brd->ports[i]; + tty_port_init(&info->port); info->board = brd; info->stop_rx = 0; info->ldisc_stop_rx = 0; @@ -2559,10 +2555,9 @@ static int __devinit mxser_initbrd(struct mxser_board *brd, process_txrx_fifo(info); info->custom_divisor = info->baud_base * 16; - info->close_delay = 5 * HZ / 10; - info->closing_wait = 30 * HZ; + info->port.close_delay = 5 * HZ / 10; + info->port.closing_wait = 30 * HZ; info->normal_termios = mxvar_sdriver->init_termios; - tty_port_init(&info->port); init_waitqueue_head(&info->delta_msr_wait); memset(&info->mon_data, 0, sizeof(struct mxser_mon)); info->err_shadow = 0; diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c index 3ca8957ba327..724b2b20f4b2 100644 --- a/drivers/char/riscom8.c +++ b/drivers/char/riscom8.c @@ -1032,8 +1032,8 @@ static void rc_close(struct tty_struct *tty, struct file *filp) * the line discipline to only process XON/XOFF characters. */ tty->closing = 1; - if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, port->closing_wait); + if (port->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, port->port.closing_wait); /* * At this point we stop accepting input. To do this, we * disable the receive line status interrupts, and tell the @@ -1065,8 +1065,8 @@ static void rc_close(struct tty_struct *tty, struct file *filp) tty->closing = 0; port->port.tty = NULL; if (port->port.blocked_open) { - if (port->close_delay) - msleep_interruptible(jiffies_to_msecs(port->close_delay)); + if (port->port.close_delay) + msleep_interruptible(jiffies_to_msecs(port->port.close_delay)); wake_up_interruptible(&port->port.open_wait); } port->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); @@ -1295,8 +1295,8 @@ static int rc_set_serial_info(struct riscom_port *port, (tmp.flags & ASYNC_SPD_MASK)); if (!capable(CAP_SYS_ADMIN)) { - if ((tmp.close_delay != port->close_delay) || - (tmp.closing_wait != port->closing_wait) || + if ((tmp.close_delay != port->port.close_delay) || + (tmp.closing_wait != port->port.closing_wait) || ((tmp.flags & ~ASYNC_USR_MASK) != (port->port.flags & ~ASYNC_USR_MASK))) return -EPERM; @@ -1305,8 +1305,8 @@ static int rc_set_serial_info(struct riscom_port *port, } else { port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) | (tmp.flags & ASYNC_FLAGS)); - port->close_delay = tmp.close_delay; - port->closing_wait = tmp.closing_wait; + port->port.close_delay = tmp.close_delay; + port->port.closing_wait = tmp.closing_wait; } if (change_speed) { unsigned long flags; @@ -1331,8 +1331,8 @@ static int rc_get_serial_info(struct riscom_port *port, tmp.irq = bp->irq; tmp.flags = port->port.flags; tmp.baud_base = (RC_OSCFREQ + CD180_TPC/2) / CD180_TPC; - tmp.close_delay = port->close_delay * HZ/100; - tmp.closing_wait = port->closing_wait * HZ/100; + tmp.close_delay = port->port.close_delay * HZ/100; + tmp.closing_wait = port->port.closing_wait * HZ/100; tmp.xmit_fifo_size = CD180_NFIFO; return copy_to_user(retinfo, &tmp, sizeof(tmp)) ? -EFAULT : 0; } @@ -1549,10 +1549,8 @@ static int __init rc_init_drivers(void) } memset(rc_port, 0, sizeof(rc_port)); for (i = 0; i < RC_NPORT * RC_NBOARD; i++) { - rc_port[i].magic = RISCOM8_MAGIC; - rc_port[i].close_delay = 50 * HZ / 100; - rc_port[i].closing_wait = 3000 * HZ / 100; tty_port_init(&rc_port[i].port); + rc_port[i].magic = RISCOM8_MAGIC; } return 0; } diff --git a/drivers/char/riscom8.h b/drivers/char/riscom8.h index 29ddbab78801..c9876b3f9714 100644 --- a/drivers/char/riscom8.h +++ b/drivers/char/riscom8.h @@ -69,14 +69,12 @@ struct riscom_port { struct tty_port port; int baud_base; int timeout; - int close_delay; int custom_divisor; int xmit_head; int xmit_tail; int xmit_cnt; short wakeup_chars; short break_length; - unsigned short closing_wait; unsigned char mark_mask; unsigned char IER; unsigned char MSVR; diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c index bc35b900f9c6..e670eae2f510 100644 --- a/drivers/char/rocket.c +++ b/drivers/char/rocket.c @@ -72,6 +72,7 @@ #include #include #include +#include #include #include #include @@ -81,7 +82,7 @@ #include #include #include -#include +#include #include #include #include @@ -648,8 +649,8 @@ static void init_r_port(int board, int aiop, int chan, struct pci_dev *pci_dev) info->board = board; info->aiop = aiop; info->chan = chan; - info->closing_wait = 3000; - info->close_delay = 50; + info->port.closing_wait = 3000; + info->port.close_delay = 50; init_waitqueue_head(&info->port.open_wait); init_completion(&info->close_wait); info->flags &= ~ROCKET_MODE_MASK; @@ -1137,8 +1138,8 @@ static void rp_close(struct tty_struct *tty, struct file *filp) /* * Wait for the transmit buffer to clear */ - if (info->closing_wait != ROCKET_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, info->closing_wait); + if (info->port.closing_wait != ROCKET_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->port.closing_wait); /* * Before we drop DTR, make sure the UART transmitter * has completely drained; this is especially @@ -1168,8 +1169,8 @@ static void rp_close(struct tty_struct *tty, struct file *filp) clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); if (info->port.blocked_open) { - if (info->close_delay) { - msleep_interruptible(jiffies_to_msecs(info->close_delay)); + if (info->port.close_delay) { + msleep_interruptible(jiffies_to_msecs(info->port.close_delay)); } wake_up_interruptible(&info->port.open_wait); } else { @@ -1327,8 +1328,8 @@ static int get_config(struct r_port *info, struct rocket_config __user *retinfo) memset(&tmp, 0, sizeof (tmp)); tmp.line = info->line; tmp.flags = info->flags; - tmp.close_delay = info->close_delay; - tmp.closing_wait = info->closing_wait; + tmp.close_delay = info->port.close_delay; + tmp.closing_wait = info->port.closing_wait; tmp.port = rcktpt_io_addr[(info->line >> 5) & 3]; if (copy_to_user(retinfo, &tmp, sizeof (*retinfo))) @@ -1353,8 +1354,8 @@ static int set_config(struct r_port *info, struct rocket_config __user *new_info } info->flags = ((info->flags & ~ROCKET_FLAGS) | (new_serial.flags & ROCKET_FLAGS)); - info->close_delay = new_serial.close_delay; - info->closing_wait = new_serial.closing_wait; + info->port.close_delay = new_serial.close_delay; + info->port.closing_wait = new_serial.closing_wait; if ((info->flags & ROCKET_SPD_MASK) == ROCKET_SPD_HI) info->port.tty->alt_speed = 57600; diff --git a/drivers/char/rocket.h b/drivers/char/rocket.h index ae6b04f90c03..a8b09195ebba 100644 --- a/drivers/char/rocket.h +++ b/drivers/char/rocket.h @@ -64,8 +64,8 @@ struct rocket_version { /* * For closing_wait and closing_wait2 */ -#define ROCKET_CLOSING_WAIT_NONE 65535 -#define ROCKET_CLOSING_WAIT_INF 0 +#define ROCKET_CLOSING_WAIT_NONE ASYNC_CLOSING_WAIT_NONE +#define ROCKET_CLOSING_WAIT_INF ASYNC_CLOSING_WAIT_INF /* * Rocketport ioctls -- "RP" diff --git a/drivers/char/rocket_int.h b/drivers/char/rocket_int.h index 3affc48f6a57..21f3ff53ba32 100644 --- a/drivers/char/rocket_int.h +++ b/drivers/char/rocket_int.h @@ -1133,8 +1133,6 @@ struct r_port { unsigned int chan:3; CONTROLLER_t *ctlp; CHANNEL_t channel; - int closing_wait; - int close_delay; int intmask; int xmit_fifo_room; /* room in xmit fifo */ unsigned char *xmit_buf; diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c index 2ee4d9893757..037dc47e4cb1 100644 --- a/drivers/char/specialix.c +++ b/drivers/char/specialix.c @@ -608,9 +608,9 @@ static inline struct specialix_port * sx_get_port(struct specialix_board * bp, dprintk (SX_DEBUG_CHAN, "channel: %d\n", channel); if (channel < CD186x_NCH) { port = &sx_port[board_No(bp) * SX_NPORT + channel]; - dprintk (SX_DEBUG_CHAN, "port: %d %p flags: 0x%x\n",board_No(bp) * SX_NPORT + channel, port, port->flags & ASYNC_INITIALIZED); + dprintk (SX_DEBUG_CHAN, "port: %d %p flags: 0x%lx\n",board_No(bp) * SX_NPORT + channel, port, port->port.flags & ASYNC_INITIALIZED); - if (port->flags & ASYNC_INITIALIZED) { + if (port->port.flags & ASYNC_INITIALIZED) { dprintk (SX_DEBUG_CHAN, "port: %d %p\n", channel, port); func_exit(); return port; @@ -637,7 +637,7 @@ static inline void sx_receive_exc(struct specialix_board * bp) func_exit(); return; } - tty = port->tty; + tty = port->port.tty; status = sx_in(bp, CD186x_RCSR); @@ -673,7 +673,7 @@ static inline void sx_receive_exc(struct specialix_board * bp) dprintk(SX_DEBUG_RX, "sx%d: port %d: Handling break...\n", board_No(bp), port_No(port)); flag = TTY_BREAK; - if (port->flags & ASYNC_SAK) + if (port->port.flags & ASYNC_SAK) do_SAK(tty); } else if (status & RCSR_PE) @@ -707,7 +707,7 @@ static inline void sx_receive(struct specialix_board * bp) func_exit(); return; } - tty = port->tty; + tty = port->port.tty; count = sx_in(bp, CD186x_RDCR); dprintk (SX_DEBUG_RX, "port: %p: count: %d\n", port, count); @@ -734,7 +734,7 @@ static inline void sx_transmit(struct specialix_board * bp) return; } dprintk (SX_DEBUG_TX, "port: %p\n", port); - tty = port->tty; + tty = port->port.tty; if (port->IER & IER_TXEMPTY) { /* FIFO drained */ @@ -811,7 +811,7 @@ static inline void sx_check_modem(struct specialix_board * bp) if (!(port = sx_get_port(bp, "Modem"))) return; - tty = port->tty; + tty = port->port.tty; mcr = sx_in(bp, CD186x_MCR); printk ("mcr = %02x.\n", mcr); @@ -821,7 +821,7 @@ static inline void sx_check_modem(struct specialix_board * bp) msvr_cd = sx_in(bp, CD186x_MSVR) & MSVR_CD; if (msvr_cd) { dprintk (SX_DEBUG_SIGNALS, "Waking up guys in open.\n"); - wake_up_interruptible(&port->open_wait); + wake_up_interruptible(&port->port.open_wait); } else { dprintk (SX_DEBUG_SIGNALS, "Sending HUP.\n"); tty_hangup(tty); @@ -1030,7 +1030,7 @@ static void sx_change_speed(struct specialix_board *bp, struct specialix_port *p func_enter(); - if (!(tty = port->tty) || !tty->termios) { + if (!(tty = port->port.tty) || !tty->termios) { func_exit(); return; } @@ -1052,9 +1052,9 @@ static void sx_change_speed(struct specialix_board *bp, struct specialix_port *p baud = tty_get_baud_rate(tty); if (baud == 38400) { - if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) baud = 57600; - if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + if ((port->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) baud = 115200; } @@ -1244,7 +1244,7 @@ static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port func_enter(); - if (port->flags & ASYNC_INITIALIZED) { + if (port->port.flags & ASYNC_INITIALIZED) { func_exit(); return 0; } @@ -1268,12 +1268,12 @@ static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port spin_lock_irqsave(&port->lock, flags); - if (port->tty) - clear_bit(TTY_IO_ERROR, &port->tty->flags); + if (port->port.tty) + clear_bit(TTY_IO_ERROR, &port->port.tty->flags); port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; sx_change_speed(bp, port); - port->flags |= ASYNC_INITIALIZED; + port->port.flags |= ASYNC_INITIALIZED; spin_unlock_irqrestore(&port->lock, flags); @@ -1292,7 +1292,7 @@ static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port * func_enter(); - if (!(port->flags & ASYNC_INITIALIZED)) { + if (!(port->port.flags & ASYNC_INITIALIZED)) { func_exit(); return; } @@ -1315,7 +1315,7 @@ static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port * spin_lock_irqsave(&bp->lock, flags); sx_out(bp, CD186x_CAR, port_No(port)); - if (!(tty = port->tty) || C_HUPCL(tty)) { + if (!(tty = port->port.tty) || C_HUPCL(tty)) { /* Drop DTR */ sx_out(bp, CD186x_MSVDTR, 0); } @@ -1330,7 +1330,7 @@ static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port * spin_unlock_irqrestore(&bp->lock, flags); if (tty) set_bit(TTY_IO_ERROR, &tty->flags); - port->flags &= ~ASYNC_INITIALIZED; + port->port.flags &= ~ASYNC_INITIALIZED; if (!bp->count) sx_shutdown_board(bp); @@ -1354,9 +1354,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, * If the device is in the middle of being closed, then block * until it's done, and then try again. */ - if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { - interruptible_sleep_on(&port->close_wait); - if (port->flags & ASYNC_HUP_NOTIFY) { + if (tty_hung_up_p(filp) || port->port.flags & ASYNC_CLOSING) { + interruptible_sleep_on(&port->port.close_wait); + if (port->port.flags & ASYNC_HUP_NOTIFY) { func_exit(); return -EAGAIN; } else { @@ -1371,7 +1371,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, */ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { - port->flags |= ASYNC_NORMAL_ACTIVE; + port->port.flags |= ASYNC_NORMAL_ACTIVE; func_exit(); return 0; } @@ -1387,13 +1387,13 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, * exit, either normal or abnormal. */ retval = 0; - add_wait_queue(&port->open_wait, &wait); + add_wait_queue(&port->port.open_wait, &wait); spin_lock_irqsave(&port->lock, flags); if (!tty_hung_up_p(filp)) { - port->count--; + port->port.count--; } spin_unlock_irqrestore(&port->lock, flags); - port->blocked_open++; + port->port.blocked_open++; while (1) { spin_lock_irqsave(&bp->lock, flags); sx_out(bp, CD186x_CAR, port_No(port)); @@ -1410,14 +1410,14 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, spin_unlock_irqrestore(&bp->lock, flags); set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || - !(port->flags & ASYNC_INITIALIZED)) { - if (port->flags & ASYNC_HUP_NOTIFY) + !(port->port.flags & ASYNC_INITIALIZED)) { + if (port->port.flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; break; } - if (!(port->flags & ASYNC_CLOSING) && + if (!(port->port.flags & ASYNC_CLOSING) && (do_clocal || CD)) break; if (signal_pending(current)) { @@ -1428,19 +1428,19 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, } set_current_state(TASK_RUNNING); - remove_wait_queue(&port->open_wait, &wait); + remove_wait_queue(&port->port.open_wait, &wait); spin_lock_irqsave(&port->lock, flags); if (!tty_hung_up_p(filp)) { - port->count++; + port->port.count++; } - port->blocked_open--; + port->port.blocked_open--; spin_unlock_irqrestore(&port->lock, flags); if (retval) { func_exit(); return retval; } - port->flags |= ASYNC_NORMAL_ACTIVE; + port->port.flags |= ASYNC_NORMAL_ACTIVE; func_exit(); return 0; } @@ -1484,10 +1484,10 @@ static int sx_open(struct tty_struct * tty, struct file * filp) } spin_lock_irqsave(&bp->lock, flags); - port->count++; + port->port.count++; bp->count++; tty->driver_data = port; - port->tty = tty; + port->port.tty = tty; spin_unlock_irqrestore(&bp->lock, flags); if ((error = sx_setup_port(bp, port))) { @@ -1547,15 +1547,15 @@ static void sx_close(struct tty_struct * tty, struct file * filp) } bp = port_Board(port); - if ((tty->count == 1) && (port->count != 1)) { + if ((tty->count == 1) && (port->port.count != 1)) { printk(KERN_ERR "sx%d: sx_close: bad port count;" " tty->count is 1, port count is %d\n", - board_No(bp), port->count); - port->count = 1; + board_No(bp), port->port.count); + port->port.count = 1; } - if (port->count > 1) { - port->count--; + if (port->port.count > 1) { + port->port.count--; bp->count--; spin_unlock_irqrestore(&port->lock, flags); @@ -1563,7 +1563,7 @@ static void sx_close(struct tty_struct * tty, struct file * filp) func_exit(); return; } - port->flags |= ASYNC_CLOSING; + port->port.flags |= ASYNC_CLOSING; /* * Now we wait for the transmit buffer to clear; and we notify * the line discipline to only process XON/XOFF characters. @@ -1571,8 +1571,8 @@ static void sx_close(struct tty_struct * tty, struct file * filp) tty->closing = 1; spin_unlock_irqrestore(&port->lock, flags); dprintk (SX_DEBUG_OPEN, "Closing\n"); - if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) { - tty_wait_until_sent(tty, port->closing_wait); + if (port->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) { + tty_wait_until_sent(tty, port->port.closing_wait); } /* * At this point we stop accepting input. To do this, we @@ -1582,7 +1582,7 @@ static void sx_close(struct tty_struct * tty, struct file * filp) */ dprintk (SX_DEBUG_OPEN, "Closed\n"); port->IER &= ~IER_RXD; - if (port->flags & ASYNC_INITIALIZED) { + if (port->port.flags & ASYNC_INITIALIZED) { port->IER &= ~IER_TXRDY; port->IER |= IER_TXEMPTY; spin_lock_irqsave(&bp->lock, flags); @@ -1611,10 +1611,10 @@ static void sx_close(struct tty_struct * tty, struct file * filp) board_No(bp), bp->count, tty->index); bp->count = 0; } - if (--port->count < 0) { + if (--port->port.count < 0) { printk(KERN_ERR "sx%d: sx_close: bad port count for tty%d: %d\n", - board_No(bp), port_No(port), port->count); - port->count = 0; + board_No(bp), port_No(port), port->port.count); + port->port.count = 0; } sx_shutdown_port(bp, port); @@ -1622,16 +1622,16 @@ static void sx_close(struct tty_struct * tty, struct file * filp) tty_ldisc_flush(tty); spin_lock_irqsave(&port->lock, flags); tty->closing = 0; - port->tty = NULL; + port->port.tty = NULL; spin_unlock_irqrestore(&port->lock, flags); - if (port->blocked_open) { - if (port->close_delay) { - msleep_interruptible(jiffies_to_msecs(port->close_delay)); + if (port->port.blocked_open) { + if (port->port.close_delay) { + msleep_interruptible(jiffies_to_msecs(port->port.close_delay)); } - wake_up_interruptible(&port->open_wait); + wake_up_interruptible(&port->port.open_wait); } - port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&port->close_wait); + port->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); + wake_up_interruptible(&port->port.close_wait); func_exit(); } @@ -1815,7 +1815,7 @@ static int sx_tiocmget(struct tty_struct *tty, struct file *file) dprintk (SX_DEBUG_INIT, "Got msvr[%d] = %02x, car = %d.\n", port_No(port), status, sx_in (bp, CD186x_CAR)); dprintk (SX_DEBUG_INIT, "sx_port = %p, port = %p\n", sx_port, port); - if (SX_CRTSCTS(port->tty)) { + if (SX_CRTSCTS(port->port.tty)) { result = /* (status & MSVR_RTS) ? */ TIOCM_DTR /* : 0) */ | ((status & MSVR_DTR) ? TIOCM_RTS : 0) | ((status & MSVR_CD) ? TIOCM_CAR : 0) @@ -1857,7 +1857,7 @@ static int sx_tiocmset(struct tty_struct *tty, struct file *file, /* if (set & TIOCM_DTR) port->MSVR |= MSVR_DTR; */ - if (SX_CRTSCTS(port->tty)) { + if (SX_CRTSCTS(port->port.tty)) { if (set & TIOCM_RTS) port->MSVR |= MSVR_DTR; } else { @@ -1869,7 +1869,7 @@ static int sx_tiocmset(struct tty_struct *tty, struct file *file, port->MSVR &= ~MSVR_RTS; */ /* if (clear & TIOCM_DTR) port->MSVR &= ~MSVR_DTR; */ - if (SX_CRTSCTS(port->tty)) { + if (SX_CRTSCTS(port->port.tty)) { if (clear & TIOCM_RTS) port->MSVR &= ~MSVR_DTR; } else { @@ -1929,27 +1929,27 @@ static inline int sx_set_serial_info(struct specialix_port * port, lock_kernel(); - change_speed = ((port->flags & ASYNC_SPD_MASK) != + change_speed = ((port->port.flags & ASYNC_SPD_MASK) != (tmp.flags & ASYNC_SPD_MASK)); change_speed |= (tmp.custom_divisor != port->custom_divisor); if (!capable(CAP_SYS_ADMIN)) { - if ((tmp.close_delay != port->close_delay) || - (tmp.closing_wait != port->closing_wait) || + if ((tmp.close_delay != port->port.close_delay) || + (tmp.closing_wait != port->port.closing_wait) || ((tmp.flags & ~ASYNC_USR_MASK) != - (port->flags & ~ASYNC_USR_MASK))) { + (port->port.flags & ~ASYNC_USR_MASK))) { func_exit(); unlock_kernel(); return -EPERM; } - port->flags = ((port->flags & ~ASYNC_USR_MASK) | + port->port.flags = ((port->port.flags & ~ASYNC_USR_MASK) | (tmp.flags & ASYNC_USR_MASK)); port->custom_divisor = tmp.custom_divisor; } else { - port->flags = ((port->flags & ~ASYNC_FLAGS) | + port->port.flags = ((port->port.flags & ~ASYNC_FLAGS) | (tmp.flags & ASYNC_FLAGS)); - port->close_delay = tmp.close_delay; - port->closing_wait = tmp.closing_wait; + port->port.close_delay = tmp.close_delay; + port->port.closing_wait = tmp.closing_wait; port->custom_divisor = tmp.custom_divisor; } if (change_speed) { @@ -1975,10 +1975,10 @@ static inline int sx_get_serial_info(struct specialix_port * port, tmp.line = port - sx_port; tmp.port = bp->base; tmp.irq = bp->irq; - tmp.flags = port->flags; + tmp.flags = port->port.flags; tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC; - tmp.close_delay = port->close_delay * HZ/100; - tmp.closing_wait = port->closing_wait * HZ/100; + tmp.close_delay = port->port.close_delay * HZ/100; + tmp.closing_wait = port->port.closing_wait * HZ/100; tmp.custom_divisor = port->custom_divisor; tmp.xmit_fifo_size = CD186x_NFIFO; unlock_kernel(); @@ -2199,17 +2199,17 @@ static void sx_hangup(struct tty_struct * tty) sx_shutdown_port(bp, port); spin_lock_irqsave(&port->lock, flags); - bp->count -= port->count; + bp->count -= port->port.count; if (bp->count < 0) { printk(KERN_ERR "sx%d: sx_hangup: bad board count: %d port: %d\n", board_No(bp), bp->count, tty->index); bp->count = 0; } - port->count = 0; - port->flags &= ~ASYNC_NORMAL_ACTIVE; - port->tty = NULL; + port->port.count = 0; + port->port.flags &= ~ASYNC_NORMAL_ACTIVE; + port->port.tty = NULL; spin_unlock_irqrestore(&port->lock, flags); - wake_up_interruptible(&port->open_wait); + wake_up_interruptible(&port->port.open_wait); func_exit(); } @@ -2224,10 +2224,6 @@ static void sx_set_termios(struct tty_struct * tty, struct ktermios * old_termio if (sx_paranoia_check(port, tty->name, "sx_set_termios")) return; - if (tty->termios->c_cflag == old_termios->c_cflag && - tty->termios->c_iflag == old_termios->c_iflag) - return; - bp = port_Board(port); spin_lock_irqsave(&port->lock, flags); sx_change_speed(port_Board(port), port); @@ -2297,10 +2293,7 @@ static int sx_init_drivers(void) memset(sx_port, 0, sizeof(sx_port)); for (i = 0; i < SX_NPORT * SX_NBOARD; i++) { sx_port[i].magic = SPECIALIX_MAGIC; - sx_port[i].close_delay = 50 * HZ/100; - sx_port[i].closing_wait = 3000 * HZ/100; - init_waitqueue_head(&sx_port[i].open_wait); - init_waitqueue_head(&sx_port[i].close_wait); + tty_port_init(&sx_port[i].port); spin_lock_init(&sx_port[i].lock); } diff --git a/drivers/char/specialix_io8.h b/drivers/char/specialix_io8.h index 3f2f85bdf516..c63005274d9b 100644 --- a/drivers/char/specialix_io8.h +++ b/drivers/char/specialix_io8.h @@ -107,23 +107,17 @@ struct specialix_board { struct specialix_port { int magic; + struct tty_port port; int baud_base; int flags; - struct tty_struct * tty; - int count; - int blocked_open; int timeout; - int close_delay; unsigned char * xmit_buf; int custom_divisor; int xmit_head; int xmit_tail; int xmit_cnt; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; short wakeup_chars; short break_length; - unsigned short closing_wait; unsigned char mark_mask; unsigned char IER; unsigned char MSVR; diff --git a/drivers/char/sx.c b/drivers/char/sx.c index b1239ee48b78..d5cffcd6a572 100644 --- a/drivers/char/sx.c +++ b/drivers/char/sx.c @@ -2395,6 +2395,7 @@ static int sx_init_portstructs(int nboards, int nports) board->ports = port; for (j = 0; j < boards[i].nports; j++) { sx_dprintk(SX_DEBUG_INIT, "initing port %d\n", j); + tty_port_init(&port->gs.port); port->gs.magic = SX_MAGIC; port->gs.close_delay = HZ / 2; port->gs.closing_wait = 30 * HZ; @@ -2407,7 +2408,6 @@ static int sx_init_portstructs(int nboards, int nports) /* * Initializing wait queue */ - tty_port_init(&port->gs.port); port++; } } diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index 734098f7dfa2..9f14753fada1 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -183,8 +183,6 @@ struct mgsl_struct { struct tty_port port; int line; int hw_version; - unsigned short close_delay; - unsigned short closing_wait; /* time to wait before closing */ struct mgsl_icount icount; @@ -3142,11 +3140,11 @@ static void mgsl_close(struct tty_struct *tty, struct file * filp) /* wait for transmit data to clear all layers */ - if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) { + if (info->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) { if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):mgsl_close(%s) calling tty_wait_until_sent\n", __FILE__,__LINE__, info->device_name ); - tty_wait_until_sent(tty, info->closing_wait); + tty_wait_until_sent(tty, info->port.closing_wait); } if (info->port.flags & ASYNC_INITIALIZED) @@ -3162,8 +3160,8 @@ static void mgsl_close(struct tty_struct *tty, struct file * filp) info->port.tty = NULL; if (info->port.blocked_open) { - if (info->close_delay) { - msleep_interruptible(jiffies_to_msecs(info->close_delay)); + if (info->port.close_delay) { + msleep_interruptible(jiffies_to_msecs(info->port.close_delay)); } wake_up_interruptible(&info->port.open_wait); } @@ -4326,12 +4324,12 @@ static struct mgsl_struct* mgsl_allocate_device(void) if (!info) { printk("Error can't allocate device instance data\n"); } else { + tty_port_init(&info->port); info->magic = MGSL_MAGIC; INIT_WORK(&info->task, mgsl_bh_handler); info->max_frame_size = 4096; - info->close_delay = 5*HZ/10; - info->closing_wait = 30*HZ; - tty_port_init(&info->port); + info->port.close_delay = 5*HZ/10; + info->port.closing_wait = 30*HZ; init_waitqueue_head(&info->status_event_wait_q); init_waitqueue_head(&info->event_wait_q); spin_lock_init(&info->irq_spinlock); diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index fc71d9819165..07aa42a7f397 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -261,8 +261,6 @@ struct slgt_info { struct slgt_info *port_array[SLGT_MAX_PORTS]; int line; /* tty line instance number */ - unsigned short close_delay; - unsigned short closing_wait; /* time to wait before closing */ struct mgsl_icount icount; @@ -758,9 +756,9 @@ static void close(struct tty_struct *tty, struct file *filp) /* wait for transmit data to clear all layers */ - if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) { + if (info->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) { DBGINFO(("%s call tty_wait_until_sent\n", info->device_name)); - tty_wait_until_sent(tty, info->closing_wait); + tty_wait_until_sent(tty, info->port.closing_wait); } if (info->port.flags & ASYNC_INITIALIZED) @@ -774,8 +772,8 @@ static void close(struct tty_struct *tty, struct file *filp) info->port.tty = NULL; if (info->port.blocked_open) { - if (info->close_delay) { - msleep_interruptible(jiffies_to_msecs(info->close_delay)); + if (info->port.close_delay) { + msleep_interruptible(jiffies_to_msecs(info->port.close_delay)); } wake_up_interruptible(&info->port.open_wait); } @@ -3448,13 +3446,13 @@ static struct slgt_info *alloc_dev(int adapter_num, int port_num, struct pci_dev DBGERR(("%s device alloc failed adapter=%d port=%d\n", driver_name, adapter_num, port_num)); } else { + tty_port_init(&info->port); info->magic = MGSL_MAGIC; INIT_WORK(&info->task, bh_handler); info->max_frame_size = 4096; info->raw_rx_size = DMABUFSIZE; - info->close_delay = 5*HZ/10; - info->closing_wait = 30*HZ; - tty_port_init(&info->port); + info->port.close_delay = 5*HZ/10; + info->port.closing_wait = 30*HZ; init_waitqueue_head(&info->status_event_wait_q); init_waitqueue_head(&info->event_wait_q); spin_lock_init(&info->netlock); diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c index 5b5b292d046b..c4bc09018368 100644 --- a/drivers/char/synclinkmp.c +++ b/drivers/char/synclinkmp.c @@ -846,11 +846,11 @@ static void close(struct tty_struct *tty, struct file *filp) /* wait for transmit data to clear all layers */ - if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) { + if (info->port.closing_wait != ASYNC_CLOSING_WAIT_NONE) { if (debug_level >= DEBUG_LEVEL_INFO) printk("%s(%d):%s close() calling tty_wait_until_sent\n", __FILE__,__LINE__, info->device_name ); - tty_wait_until_sent(tty, info->closing_wait); + tty_wait_until_sent(tty, info->port.closing_wait); } if (info->port.flags & ASYNC_INITIALIZED) @@ -866,8 +866,8 @@ static void close(struct tty_struct *tty, struct file *filp) info->port.tty = NULL; if (info->port.blocked_open) { - if (info->close_delay) { - msleep_interruptible(jiffies_to_msecs(info->close_delay)); + if (info->port.close_delay) { + msleep_interruptible(jiffies_to_msecs(info->port.close_delay)); } wake_up_interruptible(&info->port.open_wait); } @@ -3802,12 +3802,12 @@ static SLMP_INFO *alloc_dev(int adapter_num, int port_num, struct pci_dev *pdev) printk("%s(%d) Error can't allocate device instance data for adapter %d, port %d\n", __FILE__,__LINE__, adapter_num, port_num); } else { + tty_port_init(&info->port); info->magic = MGSL_MAGIC; INIT_WORK(&info->task, bh_handler); info->max_frame_size = 4096; - info->close_delay = 5*HZ/10; - info->closing_wait = 30*HZ; - tty_port_init(&info->port); + info->port.close_delay = 5*HZ/10; + info->port.closing_wait = 30*HZ; init_waitqueue_head(&info->status_event_wait_q); init_waitqueue_head(&info->event_wait_q); spin_lock_init(&info->netlock); diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index a8cc416a23c0..82f6a8c86332 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -2094,6 +2094,8 @@ void tty_port_init(struct tty_port *port) init_waitqueue_head(&port->open_wait); init_waitqueue_head(&port->close_wait); mutex_init(&port->mutex); + port->close_delay = (50 * HZ) / 100; + port->closing_wait = (3000 * HZ) / 100; } EXPORT_SYMBOL(tty_port_init); diff --git a/include/linux/cyclades.h b/include/linux/cyclades.h index a982b74a6ee6..2d3d1e04ba92 100644 --- a/include/linux/cyclades.h +++ b/include/linux/cyclades.h @@ -567,8 +567,6 @@ struct cyclades_port { int chip_rev; int custom_divisor; u8 x_char; /* to be pushed out ASAP */ - int close_delay; - unsigned short closing_wait; int breakon; int breakoff; int xmit_head; @@ -586,8 +584,8 @@ struct cyclades_port { }; #define CLOSING_WAIT_DELAY 30*HZ -#define CY_CLOSING_WAIT_NONE 65535 -#define CY_CLOSING_WAIT_INF 0 +#define CY_CLOSING_WAIT_NONE ASYNC_CLOSING_WAIT_NONE +#define CY_CLOSING_WAIT_INF ASYNC_CLOSING_WAIT_INF #define CyMAX_CHIPS_PER_CARD 8 diff --git a/include/linux/tty.h b/include/linux/tty.h index 46008e86b191..4e5833073aa6 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -188,6 +188,8 @@ struct tty_port { unsigned long flags; /* TTY flags ASY_*/ struct mutex mutex; /* Locking */ unsigned char *xmit_buf; /* Optional buffer */ + int close_delay; /* Close port delay */ + int closing_wait; /* Delay for output */ }; /* -- cgit v1.2.3 From f233ea5c9e0d8b95e4283bf6a3436b88f6fd3586 Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Mon, 21 Jul 2008 17:05:22 +1000 Subject: md: Make mddev->array_size sector-based. This patch renames the array_size field of struct mddev_s to array_sectors and converts all instances to use units of 512 byte sectors instead of 1k blocks. Signed-off-by: Andre Noll Signed-off-by: NeilBrown --- drivers/md/faulty.c | 2 +- drivers/md/linear.c | 6 +++--- drivers/md/md.c | 12 +++++++----- drivers/md/multipath.c | 2 +- drivers/md/raid0.c | 8 ++++---- drivers/md/raid1.c | 11 ++++++----- drivers/md/raid10.c | 2 +- drivers/md/raid5.c | 16 +++++++++------- include/linux/raid/md_k.h | 2 +- 9 files changed, 33 insertions(+), 28 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/faulty.c b/drivers/md/faulty.c index d107ddceefcd..268547dbfbd3 100644 --- a/drivers/md/faulty.c +++ b/drivers/md/faulty.c @@ -297,7 +297,7 @@ static int run(mddev_t *mddev) rdev_for_each(rdev, tmp, mddev) conf->rdev = rdev; - mddev->array_size = mddev->size; + mddev->array_sectors = mddev->size * 2; mddev->private = conf; reconfig(mddev, mddev->layout, -1); diff --git a/drivers/md/linear.c b/drivers/md/linear.c index ec921f58fbb8..57644a780f16 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -256,7 +256,7 @@ static int linear_run (mddev_t *mddev) if (!conf) return 1; mddev->private = conf; - mddev->array_size = conf->array_size; + mddev->array_sectors = conf->array_size * 2; blk_queue_merge_bvec(mddev->queue, linear_mergeable_bvec); mddev->queue->unplug_fn = linear_unplug; @@ -290,8 +290,8 @@ static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev) newconf->prev = mddev_to_conf(mddev); mddev->private = newconf; mddev->raid_disks++; - mddev->array_size = newconf->array_size; - set_capacity(mddev->gendisk, mddev->array_size << 1); + mddev->array_sectors = newconf->array_size * 2; + set_capacity(mddev->gendisk, mddev->array_sectors); return 0; } diff --git a/drivers/md/md.c b/drivers/md/md.c index df13a17a9627..4bfbc1982cda 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -3704,7 +3704,7 @@ static int do_md_run(mddev_t * mddev) if (mddev->flags) md_update_sb(mddev, 0); - set_capacity(disk, mddev->array_size<<1); + set_capacity(disk, mddev->array_sectors); /* If we call blk_queue_make_request here, it will * re-initialise max_sectors etc which may have been @@ -3905,7 +3905,7 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open) export_array(mddev); - mddev->array_size = 0; + mddev->array_sectors = 0; mddev->size = 0; mddev->raid_disks = 0; mddev->recovery_cp = 0; @@ -4644,7 +4644,8 @@ static int update_size(mddev_t *mddev, sector_t num_sectors) bdev = bdget_disk(mddev->gendisk, 0); if (bdev) { mutex_lock(&bdev->bd_inode->i_mutex); - i_size_write(bdev->bd_inode, (loff_t)mddev->array_size << 10); + i_size_write(bdev->bd_inode, + (loff_t)mddev->array_sectors << 9); mutex_unlock(&bdev->bd_inode->i_mutex); bdput(bdev); } @@ -5391,10 +5392,11 @@ static int md_seq_show(struct seq_file *seq, void *v) if (!list_empty(&mddev->disks)) { if (mddev->pers) seq_printf(seq, "\n %llu blocks", - (unsigned long long)mddev->array_size); + (unsigned long long) + mddev->array_sectors / 2); else seq_printf(seq, "\n %llu blocks", - (unsigned long long)size); + (unsigned long long)size); } if (mddev->persistent) { if (mddev->major_version != 0 || diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c index 541cbe3414bd..c4779ccba1c3 100644 --- a/drivers/md/multipath.c +++ b/drivers/md/multipath.c @@ -504,7 +504,7 @@ static int multipath_run (mddev_t *mddev) /* * Ok, everything is just fine now */ - mddev->array_size = mddev->size; + mddev->array_sectors = mddev->size * 2; mddev->queue->unplug_fn = multipath_unplug; mddev->queue->backing_dev_info.congested_fn = multipath_congested; diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index 914c04ddec7c..2f30ebd8b7ab 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -293,16 +293,16 @@ static int raid0_run (mddev_t *mddev) goto out_free_conf; /* calculate array device size */ - mddev->array_size = 0; + mddev->array_sectors = 0; rdev_for_each(rdev, tmp, mddev) - mddev->array_size += rdev->size; + mddev->array_sectors += rdev->size * 2; printk("raid0 : md_size is %llu blocks.\n", - (unsigned long long)mddev->array_size); + (unsigned long long)mddev->array_sectors / 2); printk("raid0 : conf->hash_spacing is %llu blocks.\n", (unsigned long long)conf->hash_spacing); { - sector_t s = mddev->array_size; + sector_t s = mddev->array_sectors / 2; sector_t space = conf->hash_spacing; int round; conf->preshift = 0; diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 491dc2d4ad5f..03a5ab705c20 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -2043,7 +2043,7 @@ static int run(mddev_t *mddev) /* * Ok, everything is just fine now */ - mddev->array_size = mddev->size; + mddev->array_sectors = mddev->size * 2; mddev->queue->unplug_fn = raid1_unplug; mddev->queue->backing_dev_info.congested_fn = raid1_congested; @@ -2105,14 +2105,15 @@ static int raid1_resize(mddev_t *mddev, sector_t sectors) * any io in the removed space completes, but it hardly seems * worth it. */ - mddev->array_size = sectors>>1; - set_capacity(mddev->gendisk, mddev->array_size << 1); + mddev->array_sectors = sectors; + set_capacity(mddev->gendisk, mddev->array_sectors); mddev->changed = 1; - if (mddev->array_size > mddev->size && mddev->recovery_cp == MaxSector) { + if (mddev->array_sectors / 2 > mddev->size && + mddev->recovery_cp == MaxSector) { mddev->recovery_cp = mddev->size << 1; set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); } - mddev->size = mddev->array_size; + mddev->size = mddev->array_sectors / 2; mddev->resync_max_sectors = sectors; return 0; } diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index df08a9fa3a1f..2acea4025243 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -2164,7 +2164,7 @@ static int run(mddev_t *mddev) /* * Ok, everything is just fine now */ - mddev->array_size = size << (conf->chunk_shift-1); + mddev->array_sectors = size << conf->chunk_shift; mddev->resync_max_sectors = size << conf->chunk_shift; mddev->queue->unplug_fn = raid10_unplug; diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 8f4c70a53210..42a480ba767b 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -3540,7 +3540,7 @@ static sector_t reshape_request(mddev_t *mddev, sector_t sector_nr, int *skipped j == raid6_next_disk(sh->pd_idx, sh->disks)) continue; s = compute_blocknr(sh, j); - if (s < (mddev->array_size<<1)) { + if (s < mddev->array_sectors) { skipped = 1; continue; } @@ -4189,7 +4189,7 @@ static int run(mddev_t *mddev) mddev->queue->backing_dev_info.congested_data = mddev; mddev->queue->backing_dev_info.congested_fn = raid5_congested; - mddev->array_size = mddev->size * (conf->previous_raid_disks - + mddev->array_sectors = 2 * mddev->size * (conf->previous_raid_disks - conf->max_degraded); blk_queue_merge_bvec(mddev->queue, raid5_mergeable_bvec); @@ -4413,8 +4413,9 @@ static int raid5_resize(mddev_t *mddev, sector_t sectors) raid5_conf_t *conf = mddev_to_conf(mddev); sectors &= ~((sector_t)mddev->chunk_size/512 - 1); - mddev->array_size = (sectors * (mddev->raid_disks-conf->max_degraded))>>1; - set_capacity(mddev->gendisk, mddev->array_size << 1); + mddev->array_sectors = sectors * (mddev->raid_disks + - conf->max_degraded); + set_capacity(mddev->gendisk, mddev->array_sectors); mddev->changed = 1; if (sectors/2 > mddev->size && mddev->recovery_cp == MaxSector) { mddev->recovery_cp = mddev->size << 1; @@ -4547,15 +4548,16 @@ static void end_reshape(raid5_conf_t *conf) struct block_device *bdev; if (!test_bit(MD_RECOVERY_INTR, &conf->mddev->recovery)) { - conf->mddev->array_size = conf->mddev->size * + conf->mddev->array_sectors = 2 * conf->mddev->size * (conf->raid_disks - conf->max_degraded); - set_capacity(conf->mddev->gendisk, conf->mddev->array_size << 1); + set_capacity(conf->mddev->gendisk, conf->mddev->array_sectors); conf->mddev->changed = 1; bdev = bdget_disk(conf->mddev->gendisk, 0); if (bdev) { mutex_lock(&bdev->bd_inode->i_mutex); - i_size_write(bdev->bd_inode, (loff_t)conf->mddev->array_size << 10); + i_size_write(bdev->bd_inode, + (loff_t)conf->mddev->array_sectors << 9); mutex_unlock(&bdev->bd_inode->i_mutex); bdput(bdev); } diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index e37aaa41efc6..6f72b47ae41c 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -150,7 +150,7 @@ struct mddev_s int raid_disks; int max_disks; sector_t size; /* used size of component devices */ - sector_t array_size; /* exported array size */ + sector_t array_sectors; /* exported array size */ __u64 events; char uuid[16]; -- cgit v1.2.3 From d6e2215052810678bc9782fd980b52706fc71f50 Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Mon, 21 Jul 2008 17:05:25 +1000 Subject: md: linear: Make array_size sector-based and rename it to array_sectors. Signed-off-by: Andre Noll Signed-off-by: NeilBrown --- drivers/md/linear.c | 16 ++++++++-------- include/linux/raid/linear.h | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/linear.c b/drivers/md/linear.c index 57644a780f16..1cafaa959443 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -120,7 +120,7 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) return NULL; cnt = 0; - conf->array_size = 0; + conf->array_sectors = 0; rdev_for_each(rdev, tmp, mddev) { int j = rdev->raid_disk; @@ -144,7 +144,7 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9); disk->size = rdev->size; - conf->array_size += rdev->size; + conf->array_sectors += rdev->size * 2; cnt++; } @@ -153,7 +153,7 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) goto out; } - min_spacing = conf->array_size; + min_spacing = conf->array_sectors / 2; sector_div(min_spacing, PAGE_SIZE/sizeof(struct dev_info *)); /* min_spacing is the minimum spacing that will fit the hash @@ -162,7 +162,7 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) * that is larger than min_spacing as use the size of that as * the actual spacing */ - conf->hash_spacing = conf->array_size; + conf->hash_spacing = conf->array_sectors / 2; for (i=0; i < cnt-1 ; i++) { sector_t sz = 0; int j; @@ -192,7 +192,7 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) unsigned round; unsigned long base; - sz = conf->array_size >> conf->preshift; + sz = conf->array_sectors >> (conf->preshift + 1); sz += 1; /* force round-up */ base = conf->hash_spacing >> conf->preshift; round = sector_div(sz, base); @@ -219,7 +219,7 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) curr_offset = 0; i = 0; for (curr_offset = 0; - curr_offset < conf->array_size; + curr_offset < conf->array_sectors / 2; curr_offset += conf->hash_spacing) { while (i < raid_disks-1 && @@ -256,7 +256,7 @@ static int linear_run (mddev_t *mddev) if (!conf) return 1; mddev->private = conf; - mddev->array_sectors = conf->array_size * 2; + mddev->array_sectors = conf->array_sectors; blk_queue_merge_bvec(mddev->queue, linear_mergeable_bvec); mddev->queue->unplug_fn = linear_unplug; @@ -290,7 +290,7 @@ static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev) newconf->prev = mddev_to_conf(mddev); mddev->private = newconf; mddev->raid_disks++; - mddev->array_sectors = newconf->array_size * 2; + mddev->array_sectors = newconf->array_sectors; set_capacity(mddev->gendisk, mddev->array_sectors); return 0; } diff --git a/include/linux/raid/linear.h b/include/linux/raid/linear.h index ba15469daf11..7e375111d007 100644 --- a/include/linux/raid/linear.h +++ b/include/linux/raid/linear.h @@ -16,7 +16,7 @@ struct linear_private_data struct linear_private_data *prev; /* earlier version */ dev_info_t **hash_table; sector_t hash_spacing; - sector_t array_size; + sector_t array_sectors; int preshift; /* shift before dividing by hash_spacing */ dev_info_t disks[0]; }; -- cgit v1.2.3 From f2ea68cf42aafdd93393b6b8b20fc3c2b5f4390c Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 21 Jul 2008 17:05:25 +1000 Subject: md: only count actual openers as access which prevent a 'stop' Open isn't the only thing that increments ->active. e.g. reading /proc/mdstat will increment it briefly. So to avoid false positives in testing for concurrent access, introduce a new counter that counts just the number of times the md device it open. Signed-off-by: NeilBrown --- drivers/md/md.c | 9 ++++++--- include/linux/raid/md_k.h | 3 ++- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/md.c b/drivers/md/md.c index 4bfbc1982cda..450f30b6edf9 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -273,6 +273,7 @@ static mddev_t * mddev_find(dev_t unit) INIT_LIST_HEAD(&new->all_mddevs); init_timer(&new->safemode_timer); atomic_set(&new->active, 1); + atomic_set(&new->openers, 0); spin_lock_init(&new->write_lock); init_waitqueue_head(&new->sb_wait); init_waitqueue_head(&new->recovery_wait); @@ -2695,14 +2696,14 @@ array_state_store(mddev_t *mddev, const char *buf, size_t len) break; case clear: /* stopping an active array */ - if (atomic_read(&mddev->active) > 1) + if (atomic_read(&mddev->openers) > 0) return -EBUSY; err = do_md_stop(mddev, 0, 0); break; case inactive: /* stopping an active array */ if (mddev->pers) { - if (atomic_read(&mddev->active) > 1) + if (atomic_read(&mddev->openers) > 0) return -EBUSY; err = do_md_stop(mddev, 2, 0); } else @@ -3816,7 +3817,7 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open) int err = 0; struct gendisk *disk = mddev->gendisk; - if (atomic_read(&mddev->active) > 1 + is_open) { + if (atomic_read(&mddev->openers) > is_open) { printk("md: %s still in use.\n",mdname(mddev)); return -EBUSY; } @@ -5014,6 +5015,7 @@ static int md_open(struct inode *inode, struct file *file) err = 0; mddev_get(mddev); + atomic_inc(&mddev->openers); mddev_unlock(mddev); check_disk_change(inode->i_bdev); @@ -5026,6 +5028,7 @@ static int md_release(struct inode *inode, struct file * file) mddev_t *mddev = inode->i_bdev->bd_disk->private_data; BUG_ON(!mddev); + atomic_dec(&mddev->openers); mddev_put(mddev); return 0; diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index 6f72b47ae41c..4bef4791d80d 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -215,7 +215,8 @@ struct mddev_s int in_sync; /* know to not need resync */ struct mutex reconfig_mutex; - atomic_t active; + atomic_t active; /* general refcount */ + atomic_t openers; /* number of active opens */ int changed; /* true if we might need to reread partition info */ int degraded; /* whether md should consider -- cgit v1.2.3 From 4b80991c6cb9efa607bc4fd6f3ecdf5511c31bb0 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Mon, 21 Jul 2008 17:05:25 +1000 Subject: md: Protect access to mddev->disks list using RCU All modifications and most access to the mddev->disks list are made under the reconfig_mutex lock. However there are three places where the list is walked without any locking. If a reconfig happens at this time, havoc (and oops) can ensue. So use RCU to protect these accesses: - wrap them in rcu_read_{,un}lock() - use list_for_each_entry_rcu - add to the list with list_add_rcu - delete from the list with list_del_rcu - delay the 'free' with call_rcu rather than schedule_work Note that export_rdev did a list_del_init on this list. In almost all cases the entry was not in the list anymore so it was a no-op and so safe. It is no longer safe as after list_del_rcu we may not touch the list_head. An audit shows that export_rdev is called: - after unbind_rdev_from_array, in which case the delete has already been done, - after bind_rdev_to_array fails, in which case the delete isn't needed. - before the device has been put on a list at all (e.g. in add_new_disk where reading the superblock fails). - and in autorun devices after a failure when the device is on a different list. So remove the list_del_init call from export_rdev, and add it back immediately before the called to export_rdev for that last case. Note also that ->same_set is sometimes used for lists other than mddev->list (e.g. candidates). In these cases rcu is not needed. Signed-off-by: NeilBrown --- drivers/md/bitmap.c | 15 ++++++++++----- drivers/md/md.c | 30 ++++++++++++++++++------------ include/linux/raid/md_k.h | 3 +++ 3 files changed, 31 insertions(+), 17 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c index eba83e25b678..621a272a2c74 100644 --- a/drivers/md/bitmap.c +++ b/drivers/md/bitmap.c @@ -241,10 +241,10 @@ static struct page *read_sb_page(mddev_t *mddev, long offset, unsigned long inde static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait) { mdk_rdev_t *rdev; - struct list_head *tmp; mddev_t *mddev = bitmap->mddev; - rdev_for_each(rdev, tmp, mddev) + rcu_read_lock(); + rdev_for_each_rcu(rdev, mddev) if (test_bit(In_sync, &rdev->flags) && !test_bit(Faulty, &rdev->flags)) { int size = PAGE_SIZE; @@ -260,11 +260,11 @@ static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait) + (long)(page->index * (PAGE_SIZE/512)) + size/512 > 0) /* bitmap runs in to metadata */ - return -EINVAL; + goto bad_alignment; if (rdev->data_offset + mddev->size*2 > rdev->sb_start + bitmap->offset) /* data runs in to bitmap */ - return -EINVAL; + goto bad_alignment; } else if (rdev->sb_start < rdev->data_offset) { /* METADATA BITMAP DATA */ if (rdev->sb_start @@ -272,7 +272,7 @@ static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait) + page->index*(PAGE_SIZE/512) + size/512 > rdev->data_offset) /* bitmap runs in to data */ - return -EINVAL; + goto bad_alignment; } else { /* DATA METADATA BITMAP - no problems */ } @@ -282,10 +282,15 @@ static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait) size, page); } + rcu_read_unlock(); if (wait) md_super_wait(mddev); return 0; + + bad_alignment: + rcu_read_unlock(); + return -EINVAL; } static void bitmap_file_kick(struct bitmap *bitmap); diff --git a/drivers/md/md.c b/drivers/md/md.c index 450f30b6edf9..c2ff77ccec50 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -1395,15 +1395,17 @@ static struct super_type super_types[] = { static int match_mddev_units(mddev_t *mddev1, mddev_t *mddev2) { - struct list_head *tmp, *tmp2; mdk_rdev_t *rdev, *rdev2; - rdev_for_each(rdev, tmp, mddev1) - rdev_for_each(rdev2, tmp2, mddev2) + rcu_read_lock(); + rdev_for_each_rcu(rdev, mddev1) + rdev_for_each_rcu(rdev2, mddev2) if (rdev->bdev->bd_contains == - rdev2->bdev->bd_contains) + rdev2->bdev->bd_contains) { + rcu_read_unlock(); return 1; - + } + rcu_read_unlock(); return 0; } @@ -1470,7 +1472,7 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev) kobject_del(&rdev->kobj); goto fail; } - list_add(&rdev->same_set, &mddev->disks); + list_add_rcu(&rdev->same_set, &mddev->disks); bd_claim_by_disk(rdev->bdev, rdev->bdev->bd_holder, mddev->gendisk); return 0; @@ -1495,14 +1497,16 @@ static void unbind_rdev_from_array(mdk_rdev_t * rdev) return; } bd_release_from_disk(rdev->bdev, rdev->mddev->gendisk); - list_del_init(&rdev->same_set); + list_del_rcu(&rdev->same_set); printk(KERN_INFO "md: unbind<%s>\n", bdevname(rdev->bdev,b)); rdev->mddev = NULL; sysfs_remove_link(&rdev->kobj, "block"); /* We need to delay this, otherwise we can deadlock when - * writing to 'remove' to "dev/state" + * writing to 'remove' to "dev/state". We also need + * to delay it due to rcu usage. */ + synchronize_rcu(); INIT_WORK(&rdev->del_work, md_delayed_delete); kobject_get(&rdev->kobj); schedule_work(&rdev->del_work); @@ -1558,7 +1562,6 @@ static void export_rdev(mdk_rdev_t * rdev) if (rdev->mddev) MD_BUG(); free_disk_sb(rdev); - list_del_init(&rdev->same_set); #ifndef MODULE if (test_bit(AutoDetected, &rdev->flags)) md_autodetect_dev(rdev->bdev->bd_dev); @@ -4062,8 +4065,10 @@ static void autorun_devices(int part) /* on success, candidates will be empty, on error * it won't... */ - rdev_for_each_list(rdev, tmp, candidates) + rdev_for_each_list(rdev, tmp, candidates) { + list_del_init(&rdev->same_set); export_rdev(rdev); + } mddev_put(mddev); } printk(KERN_INFO "md: ... autorun DONE.\n"); @@ -5529,12 +5534,12 @@ int unregister_md_personality(struct mdk_personality *p) static int is_mddev_idle(mddev_t *mddev) { mdk_rdev_t * rdev; - struct list_head *tmp; int idle; long curr_events; idle = 1; - rdev_for_each(rdev, tmp, mddev) { + rcu_read_lock(); + rdev_for_each_rcu(rdev, mddev) { struct gendisk *disk = rdev->bdev->bd_contains->bd_disk; curr_events = disk_stat_read(disk, sectors[0]) + disk_stat_read(disk, sectors[1]) - @@ -5566,6 +5571,7 @@ static int is_mddev_idle(mddev_t *mddev) idle = 0; } } + rcu_read_unlock(); return idle; } diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index 4bef4791d80d..9f2549ac0e2d 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -339,6 +339,9 @@ static inline char * mdname (mddev_t * mddev) #define rdev_for_each(rdev, tmp, mddev) \ rdev_for_each_list(rdev, tmp, (mddev)->disks) +#define rdev_for_each_rcu(rdev, mddev) \ + list_for_each_entry_rcu(rdev, &((mddev)->disks), same_set) + typedef struct mdk_thread_s { void (*run) (mddev_t *mddev); mddev_t *mddev; -- cgit v1.2.3 From f6fccb1213ba3d661baeb2a5eee0a9701dc03e1b Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Mon, 21 Jul 2008 12:00:37 +0100 Subject: dm: introduce merge_bvec_fn Introduce a bvec merge function for device mapper devices for dynamic size restrictions. This code ensures the requested biovec lies within a single target and then calls a target-specific function to check against any constraints imposed by underlying devices. Signed-off-by: Milan Broz Signed-off-by: Alasdair G Kergon --- drivers/md/dm.c | 44 +++++++++++++++++++++++++++++++++++++++++++ include/linux/device-mapper.h | 6 ++++++ include/linux/dm-ioctl.h | 4 ++-- 3 files changed, 52 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/md/dm.c b/drivers/md/dm.c index efe969074928..bca448e11878 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -829,6 +829,49 @@ static int __split_bio(struct mapped_device *md, struct bio *bio) * CRUD END *---------------------------------------------------------------*/ +static int dm_merge_bvec(struct request_queue *q, + struct bvec_merge_data *bvm, + struct bio_vec *biovec) +{ + struct mapped_device *md = q->queuedata; + struct dm_table *map = dm_get_table(md); + struct dm_target *ti; + sector_t max_sectors; + int max_size; + + if (unlikely(!map)) + return 0; + + ti = dm_table_find_target(map, bvm->bi_sector); + + /* + * Find maximum amount of I/O that won't need splitting + */ + max_sectors = min(max_io_len(md, bvm->bi_sector, ti), + (sector_t) BIO_MAX_SECTORS); + max_size = (max_sectors << SECTOR_SHIFT) - bvm->bi_size; + if (max_size < 0) + max_size = 0; + + /* + * merge_bvec_fn() returns number of bytes + * it can accept at this offset + * max is precomputed maximal io size + */ + if (max_size && ti->type->merge) + max_size = ti->type->merge(ti, bvm, biovec, max_size); + + /* + * Always allow an entire first page + */ + if (max_size <= biovec->bv_len && !(bvm->bi_size >> SECTOR_SHIFT)) + max_size = biovec->bv_len; + + dm_table_put(map); + + return max_size; +} + /* * The request function that just remaps the bio built up by * dm_merge_bvec. @@ -1032,6 +1075,7 @@ static struct mapped_device *alloc_dev(int minor) blk_queue_make_request(md->queue, dm_request); blk_queue_bounce_limit(md->queue, BLK_BOUNCE_ANY); md->queue->unplug_fn = dm_unplug_all; + blk_queue_merge_bvec(md->queue, dm_merge_bvec); md->io_pool = mempool_create_slab_pool(MIN_IOS, _io_cache); if (!md->io_pool) diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index 0d8d419d191a..a90222e3297d 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -9,11 +9,13 @@ #define _LINUX_DEVICE_MAPPER_H #include +#include struct dm_target; struct dm_table; struct dm_dev; struct mapped_device; +struct bio_vec; typedef enum { STATUSTYPE_INFO, STATUSTYPE_TABLE } status_type_t; @@ -72,6 +74,9 @@ typedef int (*dm_ioctl_fn) (struct dm_target *ti, struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); +typedef int (*dm_merge_fn) (struct dm_target *ti, struct bvec_merge_data *bvm, + struct bio_vec *biovec, int max_size); + void dm_error(const char *message); /* @@ -107,6 +112,7 @@ struct target_type { dm_status_fn status; dm_message_fn message; dm_ioctl_fn ioctl; + dm_merge_fn merge; }; struct io_restrictions { diff --git a/include/linux/dm-ioctl.h b/include/linux/dm-ioctl.h index b03c41bbfa14..28c2940eb30d 100644 --- a/include/linux/dm-ioctl.h +++ b/include/linux/dm-ioctl.h @@ -256,9 +256,9 @@ enum { #define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl) #define DM_VERSION_MAJOR 4 -#define DM_VERSION_MINOR 13 +#define DM_VERSION_MINOR 14 #define DM_VERSION_PATCHLEVEL 0 -#define DM_VERSION_EXTRA "-ioctl (2007-10-18)" +#define DM_VERSION_EXTRA "-ioctl (2008-04-23)" /* Status bits */ #define DM_READONLY_FLAG (1 << 0) /* In/Out */ -- cgit v1.2.3 From 584015727a3b88b46602b20077b46cd04f8b4ab3 Mon Sep 17 00:00:00 2001 From: Krzysztof Piotr Oledzki Date: Mon, 21 Jul 2008 10:01:34 -0700 Subject: netfilter: accounting rework: ct_extend + 64bit counters (v4) Initially netfilter has had 64bit counters for conntrack-based accounting, but it was changed in 2.6.14 to save memory. Unfortunately in-kernel 64bit counters are still required, for example for "connbytes" extension. However, 64bit counters waste a lot of memory and it was not possible to enable/disable it runtime. This patch: - reimplements accounting with respect to the extension infrastructure, - makes one global version of seq_print_acct() instead of two seq_print_counters(), - makes it possible to enable it at boot time (for CONFIG_SYSCTL/CONFIG_SYSFS=n), - makes it possible to enable/disable it at runtime by sysctl or sysfs, - extends counters from 32bit to 64bit, - renames ip_conntrack_counter -> nf_conn_counter, - enables accounting code unconditionally (no longer depends on CONFIG_NF_CT_ACCT), - set initial accounting enable state based on CONFIG_NF_CT_ACCT - removes buggy IPCT_COUNTER_FILLING event handling. If accounting is enabled newly created connections get additional acct extend. Old connections are not changed as it is not possible to add a ct_extend area to confirmed conntrack. Accounting is performed for all connections with acct extend regardless of a current state of "net.netfilter.nf_conntrack_acct". Signed-off-by: Krzysztof Piotr Oledzki Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- Documentation/feature-removal-schedule.txt | 10 ++ Documentation/kernel-parameters.txt | 7 ++ include/linux/netfilter/nf_conntrack_common.h | 8 +- include/linux/netfilter/nfnetlink_conntrack.h | 8 +- include/net/netfilter/nf_conntrack.h | 6 -- include/net/netfilter/nf_conntrack_acct.h | 51 ++++++++++ include/net/netfilter/nf_conntrack_extend.h | 2 + .../netfilter/nf_conntrack_l3proto_ipv4_compat.c | 18 +--- net/netfilter/Kconfig | 9 ++ net/netfilter/Makefile | 2 +- net/netfilter/nf_conntrack_acct.c | 104 +++++++++++++++++++++ net/netfilter/nf_conntrack_core.c | 39 +++++--- net/netfilter/nf_conntrack_netlink.c | 44 +++++---- net/netfilter/nf_conntrack_standalone.c | 18 +--- net/netfilter/xt_connbytes.c | 8 +- 15 files changed, 248 insertions(+), 86 deletions(-) create mode 100644 include/net/netfilter/nf_conntrack_acct.h create mode 100644 net/netfilter/nf_conntrack_acct.c (limited to 'include/linux') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 86334b6f8238..9f73587219e8 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -336,3 +336,13 @@ When: After the only user (hal) has seen a release with the patches Why: Over 1K .text/.data size reduction, data is available in other ways (ioctls) Who: Johannes Berg + +--------------------------- + +What: CONFIG_NF_CT_ACCT +When: 2.6.29 +Why: Accounting can now be enabled/disabled without kernel recompilation. + Currently used only to set a default value for a feature that is also + controlled by a kernel/module/sysfs/sysctl parameter. +Who: Krzysztof Piotr Oledzki + diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 09ad7450647b..e4ef27584408 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1279,6 +1279,13 @@ and is between 256 and 4096 characters. It is defined in the file This usage is only documented in each driver source file if at all. + nf_conntrack.acct= + [NETFILTER] Enable connection tracking flow accounting + 0 to disable accounting + 1 to enable accounting + Default value depends on CONFIG_NF_CT_ACCT that is + going to be removed in 2.6.29. + nfsaddrs= [NFS] See Documentation/filesystems/nfsroot.txt. diff --git a/include/linux/netfilter/nf_conntrack_common.h b/include/linux/netfilter/nf_conntrack_common.h index bad1eb760f61..885cbe282260 100644 --- a/include/linux/netfilter/nf_conntrack_common.h +++ b/include/linux/netfilter/nf_conntrack_common.h @@ -122,7 +122,7 @@ enum ip_conntrack_events IPCT_NATINFO_BIT = 10, IPCT_NATINFO = (1 << IPCT_NATINFO_BIT), - /* Counter highest bit has been set */ + /* Counter highest bit has been set, unused */ IPCT_COUNTER_FILLING_BIT = 11, IPCT_COUNTER_FILLING = (1 << IPCT_COUNTER_FILLING_BIT), @@ -145,12 +145,6 @@ enum ip_conntrack_expect_events { }; #ifdef __KERNEL__ -struct ip_conntrack_counter -{ - u_int32_t packets; - u_int32_t bytes; -}; - struct ip_conntrack_stat { unsigned int searched; diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux/netfilter/nfnetlink_conntrack.h index 759bc043dc65..c19595c89304 100644 --- a/include/linux/netfilter/nfnetlink_conntrack.h +++ b/include/linux/netfilter/nfnetlink_conntrack.h @@ -115,10 +115,10 @@ enum ctattr_protoinfo_sctp { enum ctattr_counters { CTA_COUNTERS_UNSPEC, - CTA_COUNTERS_PACKETS, /* old 64bit counters */ - CTA_COUNTERS_BYTES, /* old 64bit counters */ - CTA_COUNTERS32_PACKETS, - CTA_COUNTERS32_BYTES, + CTA_COUNTERS_PACKETS, /* 64bit counters */ + CTA_COUNTERS_BYTES, /* 64bit counters */ + CTA_COUNTERS32_PACKETS, /* old 32bit counters, unused */ + CTA_COUNTERS32_BYTES, /* old 32bit counters, unused */ __CTA_COUNTERS_MAX }; #define CTA_COUNTERS_MAX (__CTA_COUNTERS_MAX - 1) diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index 8f5b75734dd0..0741ad592da0 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -88,7 +88,6 @@ struct nf_conn_help { u8 expecting[NF_CT_MAX_EXPECT_CLASSES]; }; - #include #include @@ -111,11 +110,6 @@ struct nf_conn /* Timer function; drops refcnt when it goes off. */ struct timer_list timeout; -#ifdef CONFIG_NF_CT_ACCT - /* Accounting Information (same cache line as other written members) */ - struct ip_conntrack_counter counters[IP_CT_DIR_MAX]; -#endif - #if defined(CONFIG_NF_CONNTRACK_MARK) u_int32_t mark; #endif diff --git a/include/net/netfilter/nf_conntrack_acct.h b/include/net/netfilter/nf_conntrack_acct.h new file mode 100644 index 000000000000..5d5ae55d54c4 --- /dev/null +++ b/include/net/netfilter/nf_conntrack_acct.h @@ -0,0 +1,51 @@ +/* + * (C) 2008 Krzysztof Piotr Oledzki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _NF_CONNTRACK_ACCT_H +#define _NF_CONNTRACK_ACCT_H +#include +#include +#include +#include + +struct nf_conn_counter { + u_int64_t packets; + u_int64_t bytes; +}; + +extern int nf_ct_acct; + +static inline +struct nf_conn_counter *nf_conn_acct_find(const struct nf_conn *ct) +{ + return nf_ct_ext_find(ct, NF_CT_EXT_ACCT); +} + +static inline +struct nf_conn_counter *nf_ct_acct_ext_add(struct nf_conn *ct, gfp_t gfp) +{ + struct nf_conn_counter *acct; + + if (!nf_ct_acct) + return NULL; + + acct = nf_ct_ext_add(ct, NF_CT_EXT_ACCT, gfp); + if (!acct) + pr_debug("failed to add accounting extension area"); + + + return acct; +}; + +extern unsigned int +seq_print_acct(struct seq_file *s, const struct nf_conn *ct, int dir); + +extern int nf_conntrack_acct_init(void); +extern void nf_conntrack_acct_fini(void); + +#endif /* _NF_CONNTRACK_ACCT_H */ diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h index f80c0ed6d870..da8ee52613a5 100644 --- a/include/net/netfilter/nf_conntrack_extend.h +++ b/include/net/netfilter/nf_conntrack_extend.h @@ -7,11 +7,13 @@ enum nf_ct_ext_id { NF_CT_EXT_HELPER, NF_CT_EXT_NAT, + NF_CT_EXT_ACCT, NF_CT_EXT_NUM, }; #define NF_CT_EXT_HELPER_TYPE struct nf_conn_help #define NF_CT_EXT_NAT_TYPE struct nf_conn_nat +#define NF_CT_EXT_ACCT_TYPE struct nf_conn_counter /* Extensions: optional stuff which isn't permanently in struct. */ struct nf_ct_ext { diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c index 40a46d482490..3a020720e40b 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c @@ -18,19 +18,7 @@ #include #include #include - -#ifdef CONFIG_NF_CT_ACCT -static unsigned int -seq_print_counters(struct seq_file *s, - const struct ip_conntrack_counter *counter) -{ - return seq_printf(s, "packets=%llu bytes=%llu ", - (unsigned long long)counter->packets, - (unsigned long long)counter->bytes); -} -#else -#define seq_print_counters(x, y) 0 -#endif +#include struct ct_iter_state { unsigned int bucket; @@ -127,7 +115,7 @@ static int ct_seq_show(struct seq_file *s, void *v) l3proto, l4proto)) return -ENOSPC; - if (seq_print_counters(s, &ct->counters[IP_CT_DIR_ORIGINAL])) + if (seq_print_acct(s, ct, IP_CT_DIR_ORIGINAL)) return -ENOSPC; if (!(test_bit(IPS_SEEN_REPLY_BIT, &ct->status))) @@ -138,7 +126,7 @@ static int ct_seq_show(struct seq_file *s, void *v) l3proto, l4proto)) return -ENOSPC; - if (seq_print_counters(s, &ct->counters[IP_CT_DIR_REPLY])) + if (seq_print_acct(s, ct, IP_CT_DIR_REPLY)) return -ENOSPC; if (test_bit(IPS_ASSURED_BIT, &ct->status)) diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 316c7af1d2b1..ee898e74808d 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -49,6 +49,15 @@ config NF_CT_ACCT Those counters can be used for flow-based accounting or the `connbytes' match. + Please note that currently this option only sets a default state. + You may change it at boot time with nf_conntrack.acct=0/1 kernel + paramater or by loading the nf_conntrack module with acct=0/1. + + You may also disable/enable it on a running system with: + sysctl net.netfilter.nf_conntrack_acct=0/1 + + This option will be removed in 2.6.29. + If unsure, say `N'. config NF_CONNTRACK_MARK diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 5c4b183f6422..3bd2cc556aea 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -1,6 +1,6 @@ netfilter-objs := core.o nf_log.o nf_queue.o nf_sockopt.o -nf_conntrack-y := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_expect.o nf_conntrack_helper.o nf_conntrack_proto.o nf_conntrack_l3proto_generic.o nf_conntrack_proto_generic.o nf_conntrack_proto_tcp.o nf_conntrack_proto_udp.o nf_conntrack_extend.o +nf_conntrack-y := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_expect.o nf_conntrack_helper.o nf_conntrack_proto.o nf_conntrack_l3proto_generic.o nf_conntrack_proto_generic.o nf_conntrack_proto_tcp.o nf_conntrack_proto_udp.o nf_conntrack_extend.o nf_conntrack_acct.o nf_conntrack-$(CONFIG_NF_CONNTRACK_EVENTS) += nf_conntrack_ecache.o obj-$(CONFIG_NETFILTER) = netfilter.o diff --git a/net/netfilter/nf_conntrack_acct.c b/net/netfilter/nf_conntrack_acct.c new file mode 100644 index 000000000000..59bd8b903a19 --- /dev/null +++ b/net/netfilter/nf_conntrack_acct.c @@ -0,0 +1,104 @@ +/* Accouting handling for netfilter. */ + +/* + * (C) 2008 Krzysztof Piotr Oledzki + * + * 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 + +#ifdef CONFIG_NF_CT_ACCT +#define NF_CT_ACCT_DEFAULT 1 +#else +#define NF_CT_ACCT_DEFAULT 0 +#endif + +int nf_ct_acct __read_mostly = NF_CT_ACCT_DEFAULT; +EXPORT_SYMBOL_GPL(nf_ct_acct); + +module_param_named(acct, nf_ct_acct, bool, 0644); +MODULE_PARM_DESC(acct, "Enable connection tracking flow accounting."); + +#ifdef CONFIG_SYSCTL +static struct ctl_table_header *acct_sysctl_header; +static struct ctl_table acct_sysctl_table[] = { + { + .ctl_name = CTL_UNNUMBERED, + .procname = "nf_conntrack_acct", + .data = &nf_ct_acct, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, + {} +}; +#endif /* CONFIG_SYSCTL */ + +unsigned int +seq_print_acct(struct seq_file *s, const struct nf_conn *ct, int dir) +{ + struct nf_conn_counter *acct; + + acct = nf_conn_acct_find(ct); + if (!acct) + return 0; + + return seq_printf(s, "packets=%llu bytes=%llu ", + (unsigned long long)acct[dir].packets, + (unsigned long long)acct[dir].bytes); +}; +EXPORT_SYMBOL_GPL(seq_print_acct); + +static struct nf_ct_ext_type acct_extend __read_mostly = { + .len = sizeof(struct nf_conn_counter[IP_CT_DIR_MAX]), + .align = __alignof__(struct nf_conn_counter[IP_CT_DIR_MAX]), + .id = NF_CT_EXT_ACCT, +}; + +int nf_conntrack_acct_init(void) +{ + int ret; + +#ifdef CONFIG_NF_CT_ACCT + printk(KERN_WARNING "CONFIG_NF_CT_ACCT is deprecated and will be removed soon. Plase use\n"); + printk(KERN_WARNING "nf_conntrack.acct=1 kernel paramater, acct=1 nf_conntrack module option or\n"); + printk(KERN_WARNING "sysctl net.netfilter.nf_conntrack_acct=1 to enable it.\n"); +#endif + + ret = nf_ct_extend_register(&acct_extend); + if (ret < 0) { + printk(KERN_ERR "nf_conntrack_acct: Unable to register extension\n"); + return ret; + } + +#ifdef CONFIG_SYSCTL + acct_sysctl_header = register_sysctl_paths(nf_net_netfilter_sysctl_path, + acct_sysctl_table); + + if (!acct_sysctl_header) { + nf_ct_extend_unregister(&acct_extend); + + printk(KERN_ERR "nf_conntrack_acct: can't register to sysctl.\n"); + return -ENOMEM; + } +#endif + + return 0; +} + +void nf_conntrack_acct_fini(void) +{ +#ifdef CONFIG_SYSCTL + unregister_sysctl_table(acct_sysctl_header); +#endif + nf_ct_extend_unregister(&acct_extend); +} diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 28d03e64200b..c519d090bdb9 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -37,6 +37,7 @@ #include #include #include +#include #define NF_CONNTRACK_VERSION "0.5.0" @@ -555,6 +556,8 @@ init_conntrack(const struct nf_conntrack_tuple *tuple, return NULL; } + nf_ct_acct_ext_add(ct, GFP_ATOMIC); + spin_lock_bh(&nf_conntrack_lock); exp = nf_ct_find_expectation(tuple); if (exp) { @@ -828,17 +831,16 @@ void __nf_ct_refresh_acct(struct nf_conn *ct, } acct: -#ifdef CONFIG_NF_CT_ACCT if (do_acct) { - ct->counters[CTINFO2DIR(ctinfo)].packets++; - ct->counters[CTINFO2DIR(ctinfo)].bytes += - skb->len - skb_network_offset(skb); + struct nf_conn_counter *acct; - if ((ct->counters[CTINFO2DIR(ctinfo)].packets & 0x80000000) - || (ct->counters[CTINFO2DIR(ctinfo)].bytes & 0x80000000)) - event |= IPCT_COUNTER_FILLING; + acct = nf_conn_acct_find(ct); + if (acct) { + acct[CTINFO2DIR(ctinfo)].packets++; + acct[CTINFO2DIR(ctinfo)].bytes += + skb->len - skb_network_offset(skb); + } } -#endif spin_unlock_bh(&nf_conntrack_lock); @@ -853,15 +855,19 @@ bool __nf_ct_kill_acct(struct nf_conn *ct, const struct sk_buff *skb, int do_acct) { -#ifdef CONFIG_NF_CT_ACCT if (do_acct) { + struct nf_conn_counter *acct; + spin_lock_bh(&nf_conntrack_lock); - ct->counters[CTINFO2DIR(ctinfo)].packets++; - ct->counters[CTINFO2DIR(ctinfo)].bytes += - skb->len - skb_network_offset(skb); + acct = nf_conn_acct_find(ct); + if (acct) { + acct[CTINFO2DIR(ctinfo)].packets++; + acct[CTINFO2DIR(ctinfo)].bytes += + skb->len - skb_network_offset(skb); + } spin_unlock_bh(&nf_conntrack_lock); } -#endif + if (del_timer(&ct->timeout)) { ct->timeout.function((unsigned long)ct); return true; @@ -1029,6 +1035,7 @@ void nf_conntrack_cleanup(void) nf_conntrack_proto_fini(); nf_conntrack_helper_fini(); nf_conntrack_expect_fini(); + nf_conntrack_acct_fini(); } struct hlist_head *nf_ct_alloc_hashtable(unsigned int *sizep, int *vmalloced) @@ -1168,6 +1175,10 @@ int __init nf_conntrack_init(void) if (ret < 0) goto out_fini_expect; + ret = nf_conntrack_acct_init(); + if (ret < 0) + goto out_fini_helper; + /* For use by REJECT target */ rcu_assign_pointer(ip_ct_attach, nf_conntrack_attach); rcu_assign_pointer(nf_ct_destroy, destroy_conntrack); @@ -1180,6 +1191,8 @@ int __init nf_conntrack_init(void) return ret; +out_fini_helper: + nf_conntrack_helper_fini(); out_fini_expect: nf_conntrack_expect_fini(); out_fini_proto: diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 95a7967731f9..105a616c5c78 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -37,6 +37,7 @@ #include #include #include +#include #ifdef CONFIG_NF_NAT_NEEDED #include #include @@ -206,22 +207,26 @@ nla_put_failure: return -1; } -#ifdef CONFIG_NF_CT_ACCT static int ctnetlink_dump_counters(struct sk_buff *skb, const struct nf_conn *ct, enum ip_conntrack_dir dir) { enum ctattr_type type = dir ? CTA_COUNTERS_REPLY: CTA_COUNTERS_ORIG; struct nlattr *nest_count; + const struct nf_conn_counter *acct; + + acct = nf_conn_acct_find(ct); + if (!acct) + return 0; nest_count = nla_nest_start(skb, type | NLA_F_NESTED); if (!nest_count) goto nla_put_failure; - NLA_PUT_BE32(skb, CTA_COUNTERS32_PACKETS, - htonl(ct->counters[dir].packets)); - NLA_PUT_BE32(skb, CTA_COUNTERS32_BYTES, - htonl(ct->counters[dir].bytes)); + NLA_PUT_BE64(skb, CTA_COUNTERS_PACKETS, + cpu_to_be64(acct[dir].packets)); + NLA_PUT_BE64(skb, CTA_COUNTERS_BYTES, + cpu_to_be64(acct[dir].bytes)); nla_nest_end(skb, nest_count); @@ -230,9 +235,6 @@ ctnetlink_dump_counters(struct sk_buff *skb, const struct nf_conn *ct, nla_put_failure: return -1; } -#else -#define ctnetlink_dump_counters(a, b, c) (0) -#endif #ifdef CONFIG_NF_CONNTRACK_MARK static inline int @@ -501,11 +503,6 @@ static int ctnetlink_conntrack_event(struct notifier_block *this, goto nla_put_failure; #endif - if (events & IPCT_COUNTER_FILLING && - (ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 || - ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0)) - goto nla_put_failure; - if (events & IPCT_RELATED && ctnetlink_dump_master(skb, ct) < 0) goto nla_put_failure; @@ -576,11 +573,15 @@ restart: cb->args[1] = (unsigned long)ct; goto out; } -#ifdef CONFIG_NF_CT_ACCT + if (NFNL_MSG_TYPE(cb->nlh->nlmsg_type) == - IPCTNL_MSG_CT_GET_CTRZERO) - memset(&ct->counters, 0, sizeof(ct->counters)); -#endif + IPCTNL_MSG_CT_GET_CTRZERO) { + struct nf_conn_counter *acct; + + acct = nf_conn_acct_find(ct); + if (acct) + memset(acct, 0, sizeof(struct nf_conn_counter[IP_CT_DIR_MAX])); + } } if (cb->args[1]) { cb->args[1] = 0; @@ -832,14 +833,9 @@ ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb, u_int8_t u3 = nfmsg->nfgen_family; int err = 0; - if (nlh->nlmsg_flags & NLM_F_DUMP) { -#ifndef CONFIG_NF_CT_ACCT - if (NFNL_MSG_TYPE(nlh->nlmsg_type) == IPCTNL_MSG_CT_GET_CTRZERO) - return -ENOTSUPP; -#endif + if (nlh->nlmsg_flags & NLM_F_DUMP) return netlink_dump_start(ctnl, skb, nlh, ctnetlink_dump_table, ctnetlink_done); - } if (cda[CTA_TUPLE_ORIG]) err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_ORIG, u3); @@ -1152,6 +1148,8 @@ ctnetlink_create_conntrack(struct nlattr *cda[], goto err; } + nf_ct_acct_ext_add(ct, GFP_KERNEL); + #if defined(CONFIG_NF_CONNTRACK_MARK) if (cda[CTA_MARK]) ct->mark = ntohl(nla_get_be32(cda[CTA_MARK])); diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c index 46ea542d0df9..869ef9349d0f 100644 --- a/net/netfilter/nf_conntrack_standalone.c +++ b/net/netfilter/nf_conntrack_standalone.c @@ -25,6 +25,7 @@ #include #include #include +#include MODULE_LICENSE("GPL"); @@ -38,19 +39,6 @@ print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple, } EXPORT_SYMBOL_GPL(print_tuple); -#ifdef CONFIG_NF_CT_ACCT -static unsigned int -seq_print_counters(struct seq_file *s, - const struct ip_conntrack_counter *counter) -{ - return seq_printf(s, "packets=%llu bytes=%llu ", - (unsigned long long)counter->packets, - (unsigned long long)counter->bytes); -} -#else -#define seq_print_counters(x, y) 0 -#endif - struct ct_iter_state { unsigned int bucket; }; @@ -146,7 +134,7 @@ static int ct_seq_show(struct seq_file *s, void *v) l3proto, l4proto)) return -ENOSPC; - if (seq_print_counters(s, &ct->counters[IP_CT_DIR_ORIGINAL])) + if (seq_print_acct(s, ct, IP_CT_DIR_ORIGINAL)) return -ENOSPC; if (!(test_bit(IPS_SEEN_REPLY_BIT, &ct->status))) @@ -157,7 +145,7 @@ static int ct_seq_show(struct seq_file *s, void *v) l3proto, l4proto)) return -ENOSPC; - if (seq_print_counters(s, &ct->counters[IP_CT_DIR_REPLY])) + if (seq_print_acct(s, ct, IP_CT_DIR_REPLY)) return -ENOSPC; if (test_bit(IPS_ASSURED_BIT, &ct->status)) diff --git a/net/netfilter/xt_connbytes.c b/net/netfilter/xt_connbytes.c index d7e8983cd37f..3e39c4fe1931 100644 --- a/net/netfilter/xt_connbytes.c +++ b/net/netfilter/xt_connbytes.c @@ -8,6 +8,7 @@ #include #include #include +#include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Harald Welte "); @@ -27,12 +28,15 @@ connbytes_mt(const struct sk_buff *skb, const struct net_device *in, u_int64_t what = 0; /* initialize to make gcc happy */ u_int64_t bytes = 0; u_int64_t pkts = 0; - const struct ip_conntrack_counter *counters; + const struct nf_conn_counter *counters; ct = nf_ct_get(skb, &ctinfo); if (!ct) return false; - counters = ct->counters; + + counters = nf_conn_acct_find(ct); + if (!counters) + return false; switch (sinfo->what) { case XT_CONNBYTES_PKTS: -- cgit v1.2.3 From 72961ecf84d67d6359a1b30f9b2a8427f13e1e71 Mon Sep 17 00:00:00 2001 From: Eric Leblond Date: Mon, 21 Jul 2008 10:02:35 -0700 Subject: netfilter: nfnetlink_log: send complete hardware header This patch adds some fields to NFLOG to be able to send the complete hardware header with all necessary informations. It sends to userspace: * the type of hardware link * the lenght of hardware header * the hardware header Signed-off-by: Eric Leblond Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- include/linux/netfilter/nfnetlink_log.h | 3 +++ net/netfilter/nfnetlink_log.c | 8 ++++++++ 2 files changed, 11 insertions(+) (limited to 'include/linux') diff --git a/include/linux/netfilter/nfnetlink_log.h b/include/linux/netfilter/nfnetlink_log.h index a85721332924..f661731f3cb1 100644 --- a/include/linux/netfilter/nfnetlink_log.h +++ b/include/linux/netfilter/nfnetlink_log.h @@ -48,6 +48,9 @@ enum nfulnl_attr_type { NFULA_SEQ, /* instance-local sequence number */ NFULA_SEQ_GLOBAL, /* global sequence number */ NFULA_GID, /* group id of socket */ + NFULA_HWTYPE, /* hardware type */ + NFULA_HWHEADER, /* hardware header */ + NFULA_HWLEN, /* hardware header length */ __NFULA_MAX }; diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index b8173af8c24a..9a35b57ab76d 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -453,6 +453,14 @@ __build_packet_message(struct nfulnl_instance *inst, } } + if (indev && skb_mac_header_was_set(skb)) { + NLA_PUT_BE16(inst->skb, NFULA_HWTYPE, htons(skb->dev->type)); + NLA_PUT_BE16(inst->skb, NFULA_HWLEN, + htons(skb->dev->hard_header_len)); + NLA_PUT(inst->skb, NFULA_HWHEADER, skb->dev->hard_header_len, + skb_mac_header(skb)); + } + if (skb->tstamp.tv64) { struct nfulnl_msg_packet_timestamp ts; struct timeval tv = ktime_to_timeval(skb->tstamp); -- cgit v1.2.3 From 1b26da1510c02a2dac33c0ea48904256dcec4617 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 2 Jul 2008 12:46:22 -0700 Subject: USB: handle pci_name() being const This changes usb_create_hcd() to be able to handle the fact that pci_name() has changed to a constant string. Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 2 +- drivers/usb/core/hcd.h | 2 +- include/linux/usb.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 42a436478b78..07f7bedf4dca 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1764,7 +1764,7 @@ EXPORT_SYMBOL_GPL (usb_hc_died); * If memory is unavailable, returns NULL. */ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, - struct device *dev, char *bus_name) + struct device *dev, const char *bus_name) { struct usb_hcd *hcd; diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index b9de1569b39e..50b8bb2d1212 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -235,7 +235,7 @@ extern void usb_hcd_disable_endpoint(struct usb_device *udev, extern int usb_hcd_get_frame_number(struct usb_device *udev); extern struct usb_hcd *usb_create_hcd(const struct hc_driver *driver, - struct device *dev, char *bus_name); + struct device *dev, const char *bus_name); extern struct usb_hcd *usb_get_hcd(struct usb_hcd *hcd); extern void usb_put_hcd(struct usb_hcd *hcd); extern int usb_add_hcd(struct usb_hcd *hcd, diff --git a/include/linux/usb.h b/include/linux/usb.h index c08689ea9b4b..cee7fbb2b605 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -293,7 +293,7 @@ struct usb_devmap { struct usb_bus { struct device *controller; /* host/master side hardware */ int busnum; /* Bus number (in order of reg) */ - char *bus_name; /* stable id (PCI slot_name etc) */ + const char *bus_name; /* stable id (PCI slot_name etc) */ u8 uses_dma; /* Does the host controller use DMA? */ u8 otg_port; /* 0, or number of OTG/HNP port */ unsigned is_b_host:1; /* true during some HNP roleswitches */ -- cgit v1.2.3 From 9da82bd4649334817ef0e752a69eb99051645dad Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 8 May 2008 11:54:37 -0400 Subject: USB: implement "soft" unbinding This patch (as1091) changes the way usbcore handles interface unbinding. If the interface's driver supports "soft" unbinding (a new flag in the driver structure) then in-flight URBs are not cancelled and endpoints are not disabled. Instead the driver is allowed to continue communicating with the device (although of course it should stop before its disconnect routine returns). The purpose of this change is to allow drivers to do a clean shutdown when they get unbound from a device that is still plugged in. Killing all the URBs and disabling the endpoints before calling the driver's disconnect method doesn't give the driver any control over what happens, and it can leave devices in indeterminate states. For example, when usb-storage unbinds it doesn't want to stop while in the middle of transmitting a SCSI command. The soft_unbind flag is added because in the past, a number of drivers have experienced problems related to ongoing I/O after their disconnect routine returned. Hence "soft" unbinding is made available only to drivers that claim to support it. The patch also replaces "interface_to_usbdev(intf)" with "udev" in a couple of places, a minor simplification. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 11 ++++++----- include/linux/usb.h | 3 +++ 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 0a0e8cea0afc..8da1a56659be 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -257,15 +257,16 @@ static int usb_unbind_interface(struct device *dev) udev = interface_to_usbdev(intf); error = usb_autoresume_device(udev); - /* release all urbs for this interface */ - usb_disable_interface(interface_to_usbdev(intf), intf); + /* Terminate all URBs for this interface unless the driver + * supports "soft" unbinding. + */ + if (!driver->soft_unbind) + usb_disable_interface(udev, intf); driver->disconnect(intf); /* reset other interface state */ - usb_set_interface(interface_to_usbdev(intf), - intf->altsetting[0].desc.bInterfaceNumber, - 0); + usb_set_interface(udev, intf->altsetting[0].desc.bInterfaceNumber, 0); usb_set_intfdata(intf, NULL); intf->condition = USB_INTERFACE_UNBOUND; diff --git a/include/linux/usb.h b/include/linux/usb.h index cee7fbb2b605..8429d08bd2fd 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -972,6 +972,8 @@ struct usbdrv_wrap { * added to this driver by preventing the sysfs file from being created. * @supports_autosuspend: if set to 0, the USB core will not allow autosuspend * for interfaces bound to this driver. + * @soft_unbind: if set to 1, the USB core will not kill URBs and disable + * endpoints before calling the driver's disconnect method. * * USB interface drivers must provide a name, probe() and disconnect() * methods, and an id_table. Other driver fields are optional. @@ -1012,6 +1014,7 @@ struct usb_driver { struct usbdrv_wrap drvwrap; unsigned int no_dynamic_id:1; unsigned int supports_autosuspend:1; + unsigned int soft_unbind:1; }; #define to_usb_driver(d) container_of(d, struct usb_driver, drvwrap.driver) -- cgit v1.2.3 From ea05af61a874ffbc158d9cf06df8a9396f299f38 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 20 May 2008 01:00:46 +0300 Subject: USB: remove CVS keywords This patch removes CVS keywords that weren't updated for a long time from comments. Signed-off-by: Adrian Bunk Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devices.c | 2 -- drivers/usb/core/devio.c | 2 -- drivers/usb/gadget/rndis.c | 2 -- drivers/usb/gadget/rndis.h | 2 -- drivers/usb/misc/emi62.c | 2 -- drivers/usb/serial/digi_acceleport.c | 2 -- drivers/usb/storage/datafab.c | 2 -- drivers/usb/storage/debug.c | 2 -- drivers/usb/storage/debug.h | 2 -- drivers/usb/storage/dpcm.c | 2 -- drivers/usb/storage/dpcm.h | 2 -- drivers/usb/storage/freecom.c | 2 -- drivers/usb/storage/freecom.h | 2 -- drivers/usb/storage/initializers.c | 2 -- drivers/usb/storage/initializers.h | 2 -- drivers/usb/storage/isd200.c | 2 -- drivers/usb/storage/jumpshot.c | 2 -- drivers/usb/storage/protocol.c | 2 -- drivers/usb/storage/protocol.h | 2 -- drivers/usb/storage/scsiglue.c | 2 -- drivers/usb/storage/scsiglue.h | 2 -- drivers/usb/storage/sddr09.c | 1 - drivers/usb/storage/sddr09.h | 2 -- drivers/usb/storage/sddr55.c | 2 -- drivers/usb/storage/sddr55.h | 2 -- drivers/usb/storage/shuttle_usbat.c | 2 -- drivers/usb/storage/shuttle_usbat.h | 2 -- drivers/usb/storage/transport.c | 2 -- drivers/usb/storage/transport.h | 2 -- drivers/usb/storage/unusual_devs.h | 2 -- drivers/usb/storage/usb.c | 2 -- drivers/usb/storage/usb.h | 2 -- include/linux/usbdevice_fs.h | 2 -- 33 files changed, 65 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 83d9dc379d96..a681d9b92bda 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -46,8 +46,6 @@ * 2000-07-05: Ashley Montanaro * Converted file reading routine to dump to buffer once * per device, not per bus - * - * $Id: devices.c,v 1.5 2000/01/11 13:58:21 tom Exp $ */ #include diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 9218cca21043..7bee9c18b3bc 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -19,8 +19,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * $Id: devio.c,v 1.7 2000/02/01 17:28:48 fliegl Exp $ - * * This file implements the usbfs/x/y files, where * x is the bus number and y the device number. * diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index d0677f5d3cd5..20b15fcc9d49 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -1,8 +1,6 @@ /* * RNDIS MSG parser * - * Version: $Id: rndis.c,v 1.19 2004/03/25 21:33:46 robert Exp $ - * * Authors: Benedikt Spranger, Pengutronix * Robert Schwebel, Pengutronix * diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/rndis.h index 397b149f3ca7..b917b4f34ea8 100644 --- a/drivers/usb/gadget/rndis.h +++ b/drivers/usb/gadget/rndis.h @@ -1,8 +1,6 @@ /* * RNDIS Definitions for Remote NDIS * - * Version: $Id: rndis.h,v 1.15 2004/03/25 21:33:46 robert Exp $ - * * Authors: Benedikt Spranger, Pengutronix * Robert Schwebel, Pengutronix * diff --git a/drivers/usb/misc/emi62.c b/drivers/usb/misc/emi62.c index 20886c21e739..5d859ded5bbf 100644 --- a/drivers/usb/misc/emi62.c +++ b/drivers/usb/misc/emi62.c @@ -6,8 +6,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, as published by * the Free Software Foundation, version 2. - * - * $Id: emi62.c,v 1.15 2002/04/23 06:13:59 tapio Exp $ */ #include #include diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 04a56f300ea6..d96302d0d96e 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -229,8 +229,6 @@ * in case a wake up is lost. * - Following Documentation/DocBook/kernel-locking.pdf no spin locks * are held when calling copy_to/from_user or printk. -* -* $Id: digi_acceleport.c,v 1.80.1.2 2000/11/02 05:45:08 root Exp $ */ #include diff --git a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c index 579e9f52053a..17f1ae232919 100644 --- a/drivers/usb/storage/datafab.c +++ b/drivers/usb/storage/datafab.c @@ -1,6 +1,4 @@ /* Driver for Datafab USB Compact Flash reader - * - * $Id: datafab.c,v 1.7 2002/02/25 00:40:13 mdharm Exp $ * * datafab driver v0.1: * diff --git a/drivers/usb/storage/debug.c b/drivers/usb/storage/debug.c index 01e430654a13..a2b5526c9fa0 100644 --- a/drivers/usb/storage/debug.c +++ b/drivers/usb/storage/debug.c @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * Debugging Functions Source Code File * - * $Id: debug.c,v 1.9 2002/04/22 03:39:43 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/drivers/usb/storage/debug.h b/drivers/usb/storage/debug.h index 77e244a8c376..dbb985d52423 100644 --- a/drivers/usb/storage/debug.h +++ b/drivers/usb/storage/debug.h @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * Debugging Functions Header File * - * $Id: debug.h,v 1.6 2001/01/12 23:51:04 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/drivers/usb/storage/dpcm.c b/drivers/usb/storage/dpcm.c index 9a410b5a6e5b..939923471af4 100644 --- a/drivers/usb/storage/dpcm.c +++ b/drivers/usb/storage/dpcm.c @@ -1,6 +1,4 @@ /* Driver for Microtech DPCM-USB CompactFlash/SmartMedia reader - * - * $Id: dpcm.c,v 1.4 2001/06/11 02:54:25 mdharm Exp $ * * DPCM driver v0.1: * diff --git a/drivers/usb/storage/dpcm.h b/drivers/usb/storage/dpcm.h index 81b464cfcc1e..e7b7b0f120d7 100644 --- a/drivers/usb/storage/dpcm.h +++ b/drivers/usb/storage/dpcm.h @@ -1,6 +1,4 @@ /* Driver for Microtech DPCM-USB CompactFlash/SmartMedia reader - * - * $Id: dpcm.h,v 1.2 2000/08/25 00:13:51 mdharm Exp $ * * DPCM driver v0.1: * diff --git a/drivers/usb/storage/freecom.c b/drivers/usb/storage/freecom.c index f5a4e8d6a3b1..7a4d45677227 100644 --- a/drivers/usb/storage/freecom.c +++ b/drivers/usb/storage/freecom.c @@ -1,6 +1,4 @@ /* Driver for Freecom USB/IDE adaptor - * - * $Id: freecom.c,v 1.22 2002/04/22 03:39:43 mdharm Exp $ * * Freecom v0.1: * diff --git a/drivers/usb/storage/freecom.h b/drivers/usb/storage/freecom.h index 1b012d62d0a8..20d0fe6ba0c8 100644 --- a/drivers/usb/storage/freecom.h +++ b/drivers/usb/storage/freecom.h @@ -1,6 +1,4 @@ /* Driver for Freecom USB/IDE adaptor - * - * $Id: freecom.h,v 1.4 2000/08/29 14:49:15 dlbrown Exp $ * * Freecom v0.1: * diff --git a/drivers/usb/storage/initializers.c b/drivers/usb/storage/initializers.c index 187dd1e01093..4995bb595aef 100644 --- a/drivers/usb/storage/initializers.c +++ b/drivers/usb/storage/initializers.c @@ -1,6 +1,4 @@ /* Special Initializers for certain USB Mass Storage devices - * - * $Id: initializers.c,v 1.2 2000/09/06 22:35:57 mdharm Exp $ * * Current development and maintenance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) diff --git a/drivers/usb/storage/initializers.h b/drivers/usb/storage/initializers.h index ad3ffd4236c2..529327fbb06b 100644 --- a/drivers/usb/storage/initializers.h +++ b/drivers/usb/storage/initializers.h @@ -1,6 +1,4 @@ /* Header file for Special Initializers for certain USB Mass Storage devices - * - * $Id: initializers.h,v 1.1 2000/08/29 23:07:02 mdharm Exp $ * * Current development and maintenance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c index a153335f3648..383abf2516a5 100644 --- a/drivers/usb/storage/isd200.c +++ b/drivers/usb/storage/isd200.c @@ -1,6 +1,4 @@ /* Transport & Protocol Driver for In-System Design, Inc. ISD200 ASIC - * - * $Id: isd200.c,v 1.16 2002/04/22 03:39:43 mdharm Exp $ * * Current development and maintenance: * (C) 2001-2002 Björn Stenberg (bjorn@haxx.se) diff --git a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c index 61097cbb1585..df67f13c9e73 100644 --- a/drivers/usb/storage/jumpshot.c +++ b/drivers/usb/storage/jumpshot.c @@ -1,6 +1,4 @@ /* Driver for Lexar "Jumpshot" Compact Flash reader - * - * $Id: jumpshot.c,v 1.7 2002/02/25 00:40:13 mdharm Exp $ * * jumpshot driver v0.1: * diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c index b9b8ede61fb3..3b3357e20ea7 100644 --- a/drivers/usb/storage/protocol.c +++ b/drivers/usb/storage/protocol.c @@ -1,6 +1,4 @@ /* Driver for USB Mass Storage compliant devices - * - * $Id: protocol.c,v 1.14 2002/04/22 03:39:43 mdharm Exp $ * * Current development and maintenance by: * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) diff --git a/drivers/usb/storage/protocol.h b/drivers/usb/storage/protocol.h index 8737a36891ca..487056ffb516 100644 --- a/drivers/usb/storage/protocol.h +++ b/drivers/usb/storage/protocol.h @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * Protocol Functions Header File * - * $Id: protocol.h,v 1.4 2001/02/13 07:10:03 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 043b60b2ad17..b4c9e0f18a82 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * SCSI layer glue code * - * $Id: scsiglue.c,v 1.26 2002/04/22 03:39:43 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/drivers/usb/storage/scsiglue.h b/drivers/usb/storage/scsiglue.h index 737e4fa6045f..ffa1cca93d2c 100644 --- a/drivers/usb/storage/scsiglue.h +++ b/drivers/usb/storage/scsiglue.h @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * SCSI Connecting Glue Header File * - * $Id: scsiglue.h,v 1.4 2000/08/25 00:13:51 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c index 8972b17da843..c5a54b872c24 100644 --- a/drivers/usb/storage/sddr09.c +++ b/drivers/usb/storage/sddr09.c @@ -1,6 +1,5 @@ /* Driver for SanDisk SDDR-09 SmartMedia reader * - * $Id: sddr09.c,v 1.24 2002/04/22 03:39:43 mdharm Exp $ * (c) 2000, 2001 Robert Baruch (autophile@starband.net) * (c) 2002 Andries Brouwer (aeb@cwi.nl) * Developed with the assistance of: diff --git a/drivers/usb/storage/sddr09.h b/drivers/usb/storage/sddr09.h index c03089a9ec38..e50033ad7b19 100644 --- a/drivers/usb/storage/sddr09.h +++ b/drivers/usb/storage/sddr09.h @@ -1,8 +1,6 @@ /* Driver for SanDisk SDDR-09 SmartMedia reader * Header File * - * $Id: sddr09.h,v 1.5 2000/08/25 00:13:51 mdharm Exp $ - * * Current development and maintenance by: * (c) 2000 Robert Baruch (autophile@dol.net) * (c) 2002 Andries Brouwer (aeb@cwi.nl) diff --git a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c index 6d14327c921d..0d8df7577899 100644 --- a/drivers/usb/storage/sddr55.c +++ b/drivers/usb/storage/sddr55.c @@ -1,6 +1,4 @@ /* Driver for SanDisk SDDR-55 SmartMedia reader - * - * $Id:$ * * SDDR55 driver v0.1: * diff --git a/drivers/usb/storage/sddr55.h b/drivers/usb/storage/sddr55.h index d6bd32f6c9f3..a815a0470c84 100644 --- a/drivers/usb/storage/sddr55.h +++ b/drivers/usb/storage/sddr55.h @@ -1,8 +1,6 @@ /* Driver for SanDisk SDDR-55 SmartMedia reader * Header File * - * $Id:$ - * * Current development and maintenance by: * (c) 2002 Simon Munton * diff --git a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c index 570c1250f6f3..ae6d64810d2a 100644 --- a/drivers/usb/storage/shuttle_usbat.c +++ b/drivers/usb/storage/shuttle_usbat.c @@ -1,6 +1,4 @@ /* Driver for SCM Microsystems (a.k.a. Shuttle) USB-ATAPI cable - * - * $Id: shuttle_usbat.c,v 1.17 2002/04/22 03:39:43 mdharm Exp $ * * Current development and maintenance by: * (c) 2000, 2001 Robert Baruch (autophile@starband.net) diff --git a/drivers/usb/storage/shuttle_usbat.h b/drivers/usb/storage/shuttle_usbat.h index 3ddf143a1dec..d8bfc43e9044 100644 --- a/drivers/usb/storage/shuttle_usbat.h +++ b/drivers/usb/storage/shuttle_usbat.h @@ -1,8 +1,6 @@ /* Driver for SCM Microsystems USB-ATAPI cable * Header File * - * $Id: shuttle_usbat.h,v 1.5 2000/09/17 14:44:52 groovyjava Exp $ - * * Current development and maintenance by: * (c) 2000 Robert Baruch (autophile@dol.net) * (c) 2004, 2005 Daniel Drake diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 94138df557b9..08d3a13fec2c 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -1,6 +1,4 @@ /* Driver for USB Mass Storage compliant devices - * - * $Id: transport.c,v 1.47 2002/04/22 03:39:43 mdharm Exp $ * * Current development and maintenance by: * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) diff --git a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h index ada7c2f43f84..e70b88182f0e 100644 --- a/drivers/usb/storage/transport.h +++ b/drivers/usb/storage/transport.h @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * Transport Functions Header File * - * $Id: transport.h,v 1.18 2002/04/21 02:57:59 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 3ba972c20f3c..7ae69f55aa96 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * Unusual Devices File * - * $Id: unusual_devs.h,v 1.32 2002/02/25 02:41:24 mdharm Exp $ - * * Current development and maintenance by: * (c) 2000-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 6bfd99dd57aa..bfea851be985 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -1,6 +1,4 @@ /* Driver for USB Mass Storage compliant devices - * - * $Id: usb.c,v 1.75 2002/04/22 03:39:43 mdharm Exp $ * * Current development and maintenance by: * (c) 1999-2003 Matthew Dharm (mdharm-usb@one-eyed-alien.net) diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index 47906dc620db..a4ad73bd832d 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -1,8 +1,6 @@ /* Driver for USB Mass Storage compliant devices * Main Header File * - * $Id: usb.h,v 1.21 2002/04/21 02:57:59 mdharm Exp $ - * * Current development and maintenance by: * (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * diff --git a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h index 3118ede2c67b..0044d9b4cb85 100644 --- a/include/linux/usbdevice_fs.h +++ b/include/linux/usbdevice_fs.h @@ -22,8 +22,6 @@ * * History: * 0.1 04.01.2000 Created - * - * $Id: usbdevice_fs.h,v 1.1 2000/01/06 18:40:41 tom Exp $ */ /*****************************************************************************/ -- cgit v1.2.3 From a4c39c41bf3592684e36fa0dbbd4ab1a31f969b9 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Thu, 19 Jun 2008 17:52:25 -0700 Subject: usb gadget: descriptor copying support Define three new descriptor manipulation utilities, for use when setting up functions that may have multiple instances: usb_copy_descriptors() to copy a vector of descriptors usb_free_descriptors() to free the copy usb_find_endpoint() to find a copied version These will be used as follows. Functions will continue to have static tables of descriptors they update, now used as __initdata templates. When a function creates a new instance, it patches those tables with relevant interface and string IDs, plus endpoint assignments. Then it copies those morphed descriptors, associates the copies with the new function instance, and records the endpoint descriptors to use when activating the endpoints. When initialization is done, only the copies remain in memory. The copies are freed on driver removal. This ensures that each instance has descriptors which hold the right instance-specific data. Two instances in the same configuration will obviously never share the same interface IDs or use the same endpoints. Instances in different configurations won't do so either, which means this is slightly less memory-efficient in some cases. This also includes a bugfix to the epautoconf code that shows up with this usage model. It must replace the previous endpoint number when updating the template descriptors, not just mask in a few more bits. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/config.c | 76 ++++++++++++++++++++++++++++++++++++++++- drivers/usb/gadget/epautoconf.c | 1 + include/linux/usb/gadget.h | 19 +++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index a4e54b2743f0..1ca1c326392a 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -96,7 +96,7 @@ int usb_gadget_config_buf( /* config descriptor first */ if (length < USB_DT_CONFIG_SIZE || !desc) return -EINVAL; - *cp = *config; + *cp = *config; /* then interface/endpoint/class/vendor/... */ len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8*)buf, @@ -115,3 +115,77 @@ int usb_gadget_config_buf( return len; } +/** + * usb_copy_descriptors - copy a vector of USB descriptors + * @src: null-terminated vector to copy + * Context: initialization code, which may sleep + * + * This makes a copy of a vector of USB descriptors. Its primary use + * is to support usb_function objects which can have multiple copies, + * each needing different descriptors. Functions may have static + * tables of descriptors, which are used as templates and customized + * with identifiers (for interfaces, strings, endpoints, and more) + * as needed by a given function instance. + */ +struct usb_descriptor_header **__init +usb_copy_descriptors(struct usb_descriptor_header **src) +{ + struct usb_descriptor_header **tmp; + unsigned bytes; + unsigned n_desc; + void *mem; + struct usb_descriptor_header **ret; + + /* count descriptors and their sizes; then add vector size */ + for (bytes = 0, n_desc = 0, tmp = src; *tmp; tmp++, n_desc++) + bytes += (*tmp)->bLength; + bytes += (n_desc + 1) * sizeof(*tmp); + + mem = kmalloc(bytes, GFP_KERNEL); + if (!mem) + return NULL; + + /* fill in pointers starting at "tmp", + * to descriptors copied starting at "mem"; + * and return "ret" + */ + tmp = mem; + ret = mem; + mem += (n_desc + 1) * sizeof(*tmp); + while (*src) { + memcpy(mem, *src, (*src)->bLength); + *tmp = mem; + tmp++; + mem += (*src)->bLength; + src++; + } + *tmp = NULL; + + return ret; +} + +/** + * usb_find_endpoint - find a copy of an endpoint descriptor + * @src: original vector of descriptors + * @copy: copy of @src + * @ep: endpoint descriptor found in @src + * + * This returns the copy of the @match descriptor made for @copy. Its + * intended use is to help remembering the endpoint descriptor to use + * when enabling a given endpoint. + */ +struct usb_endpoint_descriptor *__init +usb_find_endpoint( + struct usb_descriptor_header **src, + struct usb_descriptor_header **copy, + struct usb_endpoint_descriptor *match +) +{ + while (*src) { + if (*src == (void *) match) + return (void *)*copy; + src++; + copy++; + } + return NULL; +} diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index 8bdad221fa91..9462e30192d8 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -159,6 +159,7 @@ ep_matches ( /* MATCH!! */ /* report address */ + desc->bEndpointAddress &= USB_DIR_IN; if (isdigit (ep->name [2])) { u8 num = simple_strtol (&ep->name [2], NULL, 10); desc->bEndpointAddress |= num; diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index cf468fbdbf8e..0ebedaec075d 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -858,6 +858,25 @@ int usb_descriptor_fillbuf(void *, unsigned, int usb_gadget_config_buf(const struct usb_config_descriptor *config, void *buf, unsigned buflen, const struct usb_descriptor_header **desc); +/* copy a NULL-terminated vector of descriptors */ +struct usb_descriptor_header **usb_copy_descriptors( + struct usb_descriptor_header **); + +/* return copy of endpoint descriptor given original descriptor set */ +struct usb_endpoint_descriptor *usb_find_endpoint( + struct usb_descriptor_header **src, + struct usb_descriptor_header **copy, + struct usb_endpoint_descriptor *match); + +/** + * usb_free_descriptors - free descriptors returned by usb_copy_descriptors() + * @v: vector of descriptors + */ +static inline void usb_free_descriptors(struct usb_descriptor_header **v) +{ + kfree(v); +} + /*-------------------------------------------------------------------------*/ /* utility wrapping a simple endpoint selection policy */ -- cgit v1.2.3 From 40982be52d8f64c3e10adce17e66ab755a4fa26b Mon Sep 17 00:00:00 2001 From: David Brownell Date: Thu, 19 Jun 2008 17:52:58 -0700 Subject: usb gadget: composite gadget core Add interfaces for composite gadget drivers, and basic implementation support behind it: - struct usb_function ... groups one or more interfaces into a function managed as one unit within a configuration, to which it's added by usb_add_function(). - struct usb_configuration ... groups one or more such functions into a configuration managed as one unit by a driver, to which it's added by usb_add_config(). These operate at either high or full/low speeds and at a given bMaxPower. - struct usb_composite_driver ... groups one or more such configurations into a gadget driver, which may be registered or unregistered. - struct usb_composite_dev ... a usb_composite_driver manages this; it wraps the usb_gadget exposed by the controller driver. This also includes some basic kerneldoc. How to use it (the short version): provide a usb_composite_driver with a bind() that calls usb_add_config() for each of the needed configurations. The configurations in turn have bind() calls, which will usb_add_function() for each function required. Each function's bind() allocates resources needed to perform its tasks, like endpoints; sometimes configurations will allocate resources too. Separate patches will convert most gadget drivers to this infrastructure. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- Documentation/DocBook/gadget.tmpl | 35 ++ drivers/usb/gadget/composite.c | 1040 +++++++++++++++++++++++++++++++++++++ include/linux/usb/composite.h | 338 ++++++++++++ 3 files changed, 1413 insertions(+) create mode 100644 drivers/usb/gadget/composite.c create mode 100644 include/linux/usb/composite.h (limited to 'include/linux') diff --git a/Documentation/DocBook/gadget.tmpl b/Documentation/DocBook/gadget.tmpl index 5a8ffa761e09..478bfe16a19d 100644 --- a/Documentation/DocBook/gadget.tmpl +++ b/Documentation/DocBook/gadget.tmpl @@ -524,6 +524,41 @@ These utilities include endpoint autoconfiguration. +Composite Device Framework + +The core API is sufficient for writing drivers for composite +USB devices (with more than one function in a given configuration), +and also multi-configuration devices (also more than one function, +but not necessarily sharing a given configuration). +There is however an optional framework which makes it easier to +reuse and combine functions. + + +Devices using this framework provide a struct +usb_composite_driver, which in turn provides one or +more struct usb_configuration instances. +Each such configuration includes at least one +struct usb_function, which packages a user +visible role such as "network link" or "mass storage device". +Management functions may also exist, such as "Device Firmware +Upgrade". + + +!Iinclude/linux/usb/composite.h +!Edrivers/usb/gadget/composite.c + + + +Composite Device Functions + +At this writing, a few of the current gadget drivers have +been converted to this framework. +Near-term plans include converting all of them, except for "gadgetfs". + + + + + Peripheral Controller Drivers diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c new file mode 100644 index 000000000000..b3d302a6a081 --- /dev/null +++ b/drivers/usb/gadget/composite.c @@ -0,0 +1,1040 @@ +/* + * composite.c - infrastructure for Composite USB Gadgets + * + * Copyright (C) 2006-2008 David Brownell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* #define VERBOSE_DEBUG */ + +#include +#include +#include +#include + +#include + + +/* + * The code in this file is utility code, used to build a gadget driver + * from one or more "function" drivers, one or more "configuration" + * objects, and a "usb_composite_driver" by gluing them together along + * with the relevant device-wide data. + */ + +/* big enough to hold our biggest descriptor */ +#define USB_BUFSIZ 512 + +static struct usb_composite_driver *composite; + +/* Some systems will need runtime overrides for the product identifers + * published in the device descriptor, either numbers or strings or both. + * String parameters are in UTF-8 (superset of ASCII's 7 bit characters). + */ + +static ushort idVendor; +module_param(idVendor, ushort, 0); +MODULE_PARM_DESC(idVendor, "USB Vendor ID"); + +static ushort idProduct; +module_param(idProduct, ushort, 0); +MODULE_PARM_DESC(idProduct, "USB Product ID"); + +static ushort bcdDevice; +module_param(bcdDevice, ushort, 0); +MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)"); + +static char *iManufacturer; +module_param(iManufacturer, charp, 0); +MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string"); + +static char *iProduct; +module_param(iProduct, charp, 0); +MODULE_PARM_DESC(iProduct, "USB Product string"); + +static char *iSerialNumber; +module_param(iSerialNumber, charp, 0); +MODULE_PARM_DESC(iSerialNumber, "SerialNumber string"); + +/*-------------------------------------------------------------------------*/ + +/** + * usb_add_function() - add a function to a configuration + * @config: the configuration + * @function: the function being added + * Context: single threaded during gadget setup + * + * After initialization, each configuration must have one or more + * functions added to it. Adding a function involves calling its @bind() + * method to allocate resources such as interface and string identifiers + * and endpoints. + * + * This function returns the value of the function's bind(), which is + * zero for success else a negative errno value. + */ +int __init usb_add_function(struct usb_configuration *config, + struct usb_function *function) +{ + int value = -EINVAL; + + DBG(config->cdev, "adding '%s'/%p to config '%s'/%p\n", + function->name, function, + config->label, config); + + if (!function->set_alt || !function->disable) + goto done; + + function->config = config; + list_add_tail(&function->list, &config->functions); + + /* REVISIT *require* function->bind? */ + if (function->bind) { + value = function->bind(config, function); + if (value < 0) { + list_del(&function->list); + function->config = NULL; + } + } else + value = 0; + + /* We allow configurations that don't work at both speeds. + * If we run into a lowspeed Linux system, treat it the same + * as full speed ... it's the function drivers that will need + * to avoid bulk and ISO transfers. + */ + if (!config->fullspeed && function->descriptors) + config->fullspeed = true; + if (!config->highspeed && function->hs_descriptors) + config->highspeed = true; + +done: + if (value) + DBG(config->cdev, "adding '%s'/%p --> %d\n", + function->name, function, value); + return value; +} + +/** + * usb_interface_id() - allocate an unused interface ID + * @config: configuration associated with the interface + * @function: function handling the interface + * Context: single threaded during gadget setup + * + * usb_interface_id() is called from usb_function.bind() callbacks to + * allocate new interface IDs. The function driver will then store that + * ID in interface, association, CDC union, and other descriptors. It + * will also handle any control requests targetted at that interface, + * particularly changing its altsetting via set_alt(). There may + * also be class-specific or vendor-specific requests to handle. + * + * All interface identifier should be allocated using this routine, to + * ensure that for example different functions don't wrongly assign + * different meanings to the same identifier. Note that since interface + * identifers are configuration-specific, functions used in more than + * one configuration (or more than once in a given configuration) need + * multiple versions of the relevant descriptors. + * + * Returns the interface ID which was allocated; or -ENODEV if no + * more interface IDs can be allocated. + */ +int __init usb_interface_id(struct usb_configuration *config, + struct usb_function *function) +{ + unsigned id = config->next_interface_id; + + if (id < MAX_CONFIG_INTERFACES) { + config->interface[id] = function; + config->next_interface_id = id + 1; + return id; + } + return -ENODEV; +} + +static int config_buf(struct usb_configuration *config, + enum usb_device_speed speed, void *buf, u8 type) +{ + struct usb_config_descriptor *c = buf; + void *next = buf + USB_DT_CONFIG_SIZE; + int len = USB_BUFSIZ - USB_DT_CONFIG_SIZE; + struct usb_function *f; + int status; + + /* write the config descriptor */ + c = buf; + c->bLength = USB_DT_CONFIG_SIZE; + c->bDescriptorType = type; + /* wTotalLength is written later */ + c->bNumInterfaces = config->next_interface_id; + c->bConfigurationValue = config->bConfigurationValue; + c->iConfiguration = config->iConfiguration; + c->bmAttributes = USB_CONFIG_ATT_ONE | config->bmAttributes; + c->bMaxPower = config->bMaxPower; + + /* There may be e.g. OTG descriptors */ + if (config->descriptors) { + status = usb_descriptor_fillbuf(next, len, + config->descriptors); + if (status < 0) + return status; + len -= status; + next += status; + } + + /* add each function's descriptors */ + list_for_each_entry(f, &config->functions, list) { + struct usb_descriptor_header **descriptors; + + if (speed == USB_SPEED_HIGH) + descriptors = f->hs_descriptors; + else + descriptors = f->descriptors; + if (!descriptors) + continue; + status = usb_descriptor_fillbuf(next, len, + (const struct usb_descriptor_header **) descriptors); + if (status < 0) + return status; + len -= status; + next += status; + } + + len = next - buf; + c->wTotalLength = cpu_to_le16(len); + return len; +} + +static int config_desc(struct usb_composite_dev *cdev, unsigned w_value) +{ + struct usb_gadget *gadget = cdev->gadget; + struct usb_configuration *c; + u8 type = w_value >> 8; + enum usb_device_speed speed = USB_SPEED_UNKNOWN; + + if (gadget_is_dualspeed(gadget)) { + int hs = 0; + + if (gadget->speed == USB_SPEED_HIGH) + hs = 1; + if (type == USB_DT_OTHER_SPEED_CONFIG) + hs = !hs; + if (hs) + speed = USB_SPEED_HIGH; + + } + + /* This is a lookup by config *INDEX* */ + w_value &= 0xff; + list_for_each_entry(c, &cdev->configs, list) { + /* ignore configs that won't work at this speed */ + if (speed == USB_SPEED_HIGH) { + if (!c->highspeed) + continue; + } else { + if (!c->fullspeed) + continue; + } + if (w_value == 0) + return config_buf(c, speed, cdev->req->buf, type); + w_value--; + } + return -EINVAL; +} + +static int count_configs(struct usb_composite_dev *cdev, unsigned type) +{ + struct usb_gadget *gadget = cdev->gadget; + struct usb_configuration *c; + unsigned count = 0; + int hs = 0; + + if (gadget_is_dualspeed(gadget)) { + if (gadget->speed == USB_SPEED_HIGH) + hs = 1; + if (type == USB_DT_DEVICE_QUALIFIER) + hs = !hs; + } + list_for_each_entry(c, &cdev->configs, list) { + /* ignore configs that won't work at this speed */ + if (hs) { + if (!c->highspeed) + continue; + } else { + if (!c->fullspeed) + continue; + } + count++; + } + return count; +} + +static void device_qual(struct usb_composite_dev *cdev) +{ + struct usb_qualifier_descriptor *qual = cdev->req->buf; + + qual->bLength = sizeof(*qual); + qual->bDescriptorType = USB_DT_DEVICE_QUALIFIER; + /* POLICY: same bcdUSB and device type info at both speeds */ + qual->bcdUSB = cdev->desc.bcdUSB; + qual->bDeviceClass = cdev->desc.bDeviceClass; + qual->bDeviceSubClass = cdev->desc.bDeviceSubClass; + qual->bDeviceProtocol = cdev->desc.bDeviceProtocol; + /* ASSUME same EP0 fifo size at both speeds */ + qual->bMaxPacketSize0 = cdev->desc.bMaxPacketSize0; + qual->bNumConfigurations = count_configs(cdev, USB_DT_DEVICE_QUALIFIER); +} + +/*-------------------------------------------------------------------------*/ + +static void reset_config(struct usb_composite_dev *cdev) +{ + struct usb_function *f; + + DBG(cdev, "reset config\n"); + + list_for_each_entry(f, &cdev->config->functions, list) { + if (f->disable) + f->disable(f); + } + cdev->config = NULL; +} + +static int set_config(struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *ctrl, unsigned number) +{ + struct usb_gadget *gadget = cdev->gadget; + struct usb_configuration *c = NULL; + int result = -EINVAL; + unsigned power = gadget_is_otg(gadget) ? 8 : 100; + int tmp; + + if (cdev->config) + reset_config(cdev); + + if (number) { + list_for_each_entry(c, &cdev->configs, list) { + if (c->bConfigurationValue == number) { + result = 0; + break; + } + } + if (result < 0) + goto done; + } else + result = 0; + + INFO(cdev, "%s speed config #%d: %s\n", + ({ char *speed; + switch (gadget->speed) { + case USB_SPEED_LOW: speed = "low"; break; + case USB_SPEED_FULL: speed = "full"; break; + case USB_SPEED_HIGH: speed = "high"; break; + default: speed = "?"; break; + } ; speed; }), number, c ? c->label : "unconfigured"); + + if (!c) + goto done; + + cdev->config = c; + + /* Initialize all interfaces by setting them to altsetting zero. */ + for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) { + struct usb_function *f = c->interface[tmp]; + + if (!f) + break; + + result = f->set_alt(f, tmp, 0); + if (result < 0) { + DBG(cdev, "interface %d (%s/%p) alt 0 --> %d\n", + tmp, f->name, f, result); + + reset_config(cdev); + goto done; + } + } + + /* when we return, be sure our power usage is valid */ + power = 2 * c->bMaxPower; +done: + usb_gadget_vbus_draw(gadget, power); + return result; +} + +/** + * usb_add_config() - add a configuration to a device. + * @cdev: wraps the USB gadget + * @config: the configuration, with bConfigurationValue assigned + * Context: single threaded during gadget setup + * + * One of the main tasks of a composite driver's bind() routine is to + * add each of the configurations it supports, using this routine. + * + * This function returns the value of the configuration's bind(), which + * is zero for success else a negative errno value. Binding configurations + * assigns global resources including string IDs, and per-configuration + * resources such as interface IDs and endpoints. + */ +int __init usb_add_config(struct usb_composite_dev *cdev, + struct usb_configuration *config) +{ + int status = -EINVAL; + struct usb_configuration *c; + + DBG(cdev, "adding config #%u '%s'/%p\n", + config->bConfigurationValue, + config->label, config); + + if (!config->bConfigurationValue || !config->bind) + goto done; + + /* Prevent duplicate configuration identifiers */ + list_for_each_entry(c, &cdev->configs, list) { + if (c->bConfigurationValue == config->bConfigurationValue) { + status = -EBUSY; + goto done; + } + } + + config->cdev = cdev; + list_add_tail(&config->list, &cdev->configs); + + INIT_LIST_HEAD(&config->functions); + config->next_interface_id = 0; + + status = config->bind(config); + if (status < 0) { + list_del(&config->list); + config->cdev = NULL; + } else { + unsigned i; + + DBG(cdev, "cfg %d/%p speeds:%s%s\n", + config->bConfigurationValue, config, + config->highspeed ? " high" : "", + config->fullspeed + ? (gadget_is_dualspeed(cdev->gadget) + ? " full" + : " full/low") + : ""); + + for (i = 0; i < MAX_CONFIG_INTERFACES; i++) { + struct usb_function *f = config->interface[i]; + + if (!f) + continue; + DBG(cdev, " interface %d = %s/%p\n", + i, f->name, f); + } + } + + /* set_alt(), or next config->bind(), sets up + * ep->driver_data as needed. + */ + usb_ep_autoconfig_reset(cdev->gadget); + +done: + if (status) + DBG(cdev, "added config '%s'/%u --> %d\n", config->label, + config->bConfigurationValue, status); + return status; +} + +/*-------------------------------------------------------------------------*/ + +/* We support strings in multiple languages ... string descriptor zero + * says which languages are supported. The typical case will be that + * only one language (probably English) is used, with I18N handled on + * the host side. + */ + +static void collect_langs(struct usb_gadget_strings **sp, __le16 *buf) +{ + const struct usb_gadget_strings *s; + u16 language; + __le16 *tmp; + + while (*sp) { + s = *sp; + language = cpu_to_le16(s->language); + for (tmp = buf; *tmp && tmp < &buf[126]; tmp++) { + if (*tmp == language) + goto repeat; + } + *tmp++ = language; +repeat: + sp++; + } +} + +static int lookup_string( + struct usb_gadget_strings **sp, + void *buf, + u16 language, + int id +) +{ + struct usb_gadget_strings *s; + int value; + + while (*sp) { + s = *sp++; + if (s->language != language) + continue; + value = usb_gadget_get_string(s, id, buf); + if (value > 0) + return value; + } + return -EINVAL; +} + +static int get_string(struct usb_composite_dev *cdev, + void *buf, u16 language, int id) +{ + struct usb_configuration *c; + struct usb_function *f; + int len; + + /* Yes, not only is USB's I18N support probably more than most + * folk will ever care about ... also, it's all supported here. + * (Except for UTF8 support for Unicode's "Astral Planes".) + */ + + /* 0 == report all available language codes */ + if (id == 0) { + struct usb_string_descriptor *s = buf; + struct usb_gadget_strings **sp; + + memset(s, 0, 256); + s->bDescriptorType = USB_DT_STRING; + + sp = composite->strings; + if (sp) + collect_langs(sp, s->wData); + + list_for_each_entry(c, &cdev->configs, list) { + sp = c->strings; + if (sp) + collect_langs(sp, s->wData); + + list_for_each_entry(f, &c->functions, list) { + sp = f->strings; + if (sp) + collect_langs(sp, s->wData); + } + } + + for (len = 0; s->wData[len] && len <= 126; len++) + continue; + if (!len) + return -EINVAL; + + s->bLength = 2 * (len + 1); + return s->bLength; + } + + /* Otherwise, look up and return a specified string. String IDs + * are device-scoped, so we look up each string table we're told + * about. These lookups are infrequent; simpler-is-better here. + */ + if (composite->strings) { + len = lookup_string(composite->strings, buf, language, id); + if (len > 0) + return len; + } + list_for_each_entry(c, &cdev->configs, list) { + if (c->strings) { + len = lookup_string(c->strings, buf, language, id); + if (len > 0) + return len; + } + list_for_each_entry(f, &c->functions, list) { + if (!f->strings) + continue; + len = lookup_string(f->strings, buf, language, id); + if (len > 0) + return len; + } + } + return -EINVAL; +} + +/** + * usb_string_id() - allocate an unused string ID + * @cdev: the device whose string descriptor IDs are being allocated + * Context: single threaded during gadget setup + * + * @usb_string_id() is called from bind() callbacks to allocate + * string IDs. Drivers for functions, configurations, or gadgets will + * then store that ID in the appropriate descriptors and string table. + * + * All string identifier should be allocated using this routine, to + * ensure that for example different functions don't wrongly assign + * different meanings to the same identifier. + */ +int __init usb_string_id(struct usb_composite_dev *cdev) +{ + if (cdev->next_string_id < 254) { + /* string id 0 is reserved */ + cdev->next_string_id++; + return cdev->next_string_id; + } + return -ENODEV; +} + +/*-------------------------------------------------------------------------*/ + +static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req) +{ + if (req->status || req->actual != req->length) + DBG((struct usb_composite_dev *) ep->driver_data, + "setup complete --> %d, %d/%d\n", + req->status, req->actual, req->length); +} + +/* + * The setup() callback implements all the ep0 functionality that's + * not handled lower down, in hardware or the hardware driver(like + * device and endpoint feature flags, and their status). It's all + * housekeeping for the gadget function we're implementing. Most of + * the work is in config and function specific setup. + */ +static int +composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_request *req = cdev->req; + int value = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + struct usb_function *f = NULL; + + /* partial re-init of the response message; the function or the + * gadget might need to intercept e.g. a control-OUT completion + * when we delegate to it. + */ + req->zero = 0; + req->complete = composite_setup_complete; + req->length = USB_BUFSIZ; + gadget->ep0->driver_data = cdev; + + switch (ctrl->bRequest) { + + /* we handle all standard USB descriptors */ + case USB_REQ_GET_DESCRIPTOR: + if (ctrl->bRequestType != USB_DIR_IN) + goto unknown; + switch (w_value >> 8) { + + case USB_DT_DEVICE: + cdev->desc.bNumConfigurations = + count_configs(cdev, USB_DT_DEVICE); + value = min(w_length, (u16) sizeof cdev->desc); + memcpy(req->buf, &cdev->desc, value); + break; + case USB_DT_DEVICE_QUALIFIER: + if (!gadget_is_dualspeed(gadget)) + break; + device_qual(cdev); + value = min_t(int, w_length, + sizeof(struct usb_qualifier_descriptor)); + break; + case USB_DT_OTHER_SPEED_CONFIG: + if (!gadget_is_dualspeed(gadget)) + break; + /* FALLTHROUGH */ + case USB_DT_CONFIG: + value = config_desc(cdev, w_value); + if (value >= 0) + value = min(w_length, (u16) value); + break; + case USB_DT_STRING: + value = get_string(cdev, req->buf, + w_index, w_value & 0xff); + if (value >= 0) + value = min(w_length, (u16) value); + break; + } + break; + + /* any number of configs can work */ + case USB_REQ_SET_CONFIGURATION: + if (ctrl->bRequestType != 0) + goto unknown; + if (gadget_is_otg(gadget)) { + if (gadget->a_hnp_support) + DBG(cdev, "HNP available\n"); + else if (gadget->a_alt_hnp_support) + DBG(cdev, "HNP on another port\n"); + else + VDBG(cdev, "HNP inactive\n"); + } + spin_lock(&cdev->lock); + value = set_config(cdev, ctrl, w_value); + spin_unlock(&cdev->lock); + break; + case USB_REQ_GET_CONFIGURATION: + if (ctrl->bRequestType != USB_DIR_IN) + goto unknown; + if (cdev->config) + *(u8 *)req->buf = cdev->config->bConfigurationValue; + else + *(u8 *)req->buf = 0; + value = min(w_length, (u16) 1); + break; + + /* function drivers must handle get/set altsetting; if there's + * no get() method, we know only altsetting zero works. + */ + case USB_REQ_SET_INTERFACE: + if (ctrl->bRequestType != USB_RECIP_INTERFACE) + goto unknown; + if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES) + break; + f = cdev->config->interface[w_index]; + if (!f) + break; + if (w_value && !f->get_alt) + break; + value = f->set_alt(f, w_index, w_value); + break; + case USB_REQ_GET_INTERFACE: + if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) + goto unknown; + if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES) + break; + f = cdev->config->interface[w_index]; + if (!f) + break; + /* lots of interfaces only need altsetting zero... */ + value = f->get_alt ? f->get_alt(f, w_index) : 0; + if (value < 0) + break; + *((u8 *)req->buf) = value; + value = min(w_length, (u16) 1); + break; + default: +unknown: + VDBG(cdev, + "non-core control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + + /* functions always handle their interfaces ... punt other + * recipients (endpoint, other, WUSB, ...) to the current + * configuration code. + * + * REVISIT it could make sense to let the composite device + * take such requests too, if that's ever needed: to work + * in config 0, etc. + */ + if ((ctrl->bRequestType & USB_RECIP_MASK) + == USB_RECIP_INTERFACE) { + f = cdev->config->interface[w_index]; + if (f && f->setup) + value = f->setup(f, ctrl); + else + f = NULL; + } + if (value < 0 && !f) { + struct usb_configuration *c; + + c = cdev->config; + if (c && c->setup) + value = c->setup(c, ctrl); + } + + goto done; + } + + /* respond with data transfer before status phase? */ + if (value >= 0) { + req->length = value; + req->zero = value < w_length; + value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); + if (value < 0) { + DBG(cdev, "ep_queue --> %d\n", value); + req->status = 0; + composite_setup_complete(gadget->ep0, req); + } + } + +done: + /* device either stalls (value < 0) or reports success */ + return value; +} + +static void composite_disconnect(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + unsigned long flags; + + /* REVISIT: should we have config and device level + * disconnect callbacks? + */ + spin_lock_irqsave(&cdev->lock, flags); + if (cdev->config) + reset_config(cdev); + spin_unlock_irqrestore(&cdev->lock, flags); +} + +/*-------------------------------------------------------------------------*/ + +static void /* __init_or_exit */ +composite_unbind(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + + /* composite_disconnect() must already have been called + * by the underlying peripheral controller driver! + * so there's no i/o concurrency that could affect the + * state protected by cdev->lock. + */ + WARN_ON(cdev->config); + + while (!list_empty(&cdev->configs)) { + struct usb_configuration *c; + + c = list_first_entry(&cdev->configs, + struct usb_configuration, list); + while (!list_empty(&c->functions)) { + struct usb_function *f; + + f = list_first_entry(&c->functions, + struct usb_function, list); + list_del(&f->list); + if (f->unbind) { + DBG(cdev, "unbind function '%s'/%p\n", + f->name, f); + f->unbind(c, f); + /* may free memory for "f" */ + } + } + list_del(&c->list); + if (c->unbind) { + DBG(cdev, "unbind config '%s'/%p\n", c->label, c); + c->unbind(c); + /* may free memory for "c" */ + } + } + if (composite->unbind) + composite->unbind(cdev); + + if (cdev->req) { + kfree(cdev->req->buf); + usb_ep_free_request(gadget->ep0, cdev->req); + } + kfree(cdev); + set_gadget_data(gadget, NULL); + composite = NULL; +} + +static void __init +string_override_one(struct usb_gadget_strings *tab, u8 id, const char *s) +{ + struct usb_string *str = tab->strings; + + for (str = tab->strings; str->s; str++) { + if (str->id == id) { + str->s = s; + return; + } + } +} + +static void __init +string_override(struct usb_gadget_strings **tab, u8 id, const char *s) +{ + while (*tab) { + string_override_one(*tab, id, s); + tab++; + } +} + +static int __init composite_bind(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev; + int status = -ENOMEM; + + cdev = kzalloc(sizeof *cdev, GFP_KERNEL); + if (!cdev) + return status; + + spin_lock_init(&cdev->lock); + cdev->gadget = gadget; + set_gadget_data(gadget, cdev); + INIT_LIST_HEAD(&cdev->configs); + + /* preallocate control response and buffer */ + cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); + if (!cdev->req) + goto fail; + cdev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL); + if (!cdev->req->buf) + goto fail; + cdev->req->complete = composite_setup_complete; + gadget->ep0->driver_data = cdev; + + cdev->bufsiz = USB_BUFSIZ; + cdev->driver = composite; + + usb_gadget_set_selfpowered(gadget); + + /* interface and string IDs start at zero via kzalloc. + * we force endpoints to start unassigned; few controller + * drivers will zero ep->driver_data. + */ + usb_ep_autoconfig_reset(cdev->gadget); + + /* composite gadget needs to assign strings for whole device (like + * serial number), register function drivers, potentially update + * power state and consumption, etc + */ + status = composite->bind(cdev); + if (status < 0) + goto fail; + + cdev->desc = *composite->dev; + cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket; + + /* standardized runtime overrides for device ID data */ + if (idVendor) + cdev->desc.idVendor = cpu_to_le16(idVendor); + if (idProduct) + cdev->desc.idProduct = cpu_to_le16(idProduct); + if (bcdDevice) + cdev->desc.bcdDevice = cpu_to_le16(bcdDevice); + + /* strings can't be assigned before bind() allocates the + * releavnt identifiers + */ + if (cdev->desc.iManufacturer && iManufacturer) + string_override(composite->strings, + cdev->desc.iManufacturer, iManufacturer); + if (cdev->desc.iProduct && iProduct) + string_override(composite->strings, + cdev->desc.iProduct, iProduct); + if (cdev->desc.iSerialNumber && iSerialNumber) + string_override(composite->strings, + cdev->desc.iSerialNumber, iSerialNumber); + + INFO(cdev, "%s ready\n", composite->name); + return 0; + +fail: + composite_unbind(gadget); + return status; +} + +/*-------------------------------------------------------------------------*/ + +static void +composite_suspend(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_function *f; + + /* REVISIT: should we have config and device level + * suspend/resume callbacks? + */ + DBG(cdev, "suspend\n"); + if (cdev->config) { + list_for_each_entry(f, &cdev->config->functions, list) { + if (f->suspend) + f->suspend(f); + } + } +} + +static void +composite_resume(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_function *f; + + /* REVISIT: should we have config and device level + * suspend/resume callbacks? + */ + DBG(cdev, "resume\n"); + if (cdev->config) { + list_for_each_entry(f, &cdev->config->functions, list) { + if (f->resume) + f->resume(f); + } + } +} + +/*-------------------------------------------------------------------------*/ + +static struct usb_gadget_driver composite_driver = { + .speed = USB_SPEED_HIGH, + + .bind = composite_bind, + .unbind = __exit_p(composite_unbind), + + .setup = composite_setup, + .disconnect = composite_disconnect, + + .suspend = composite_suspend, + .resume = composite_resume, + + .driver = { + .owner = THIS_MODULE, + }, +}; + +/** + * usb_composite_register() - register a composite driver + * @driver: the driver to register + * Context: single threaded during gadget setup + * + * This function is used to register drivers using the composite driver + * framework. The return value is zero, or a negative errno value. + * Those values normally come from the driver's @bind method, which does + * all the work of setting up the driver to match the hardware. + * + * On successful return, the gadget is ready to respond to requests from + * the host, unless one of its components invokes usb_gadget_disconnect() + * while it was binding. That would usually be done in order to wait for + * some userspace participation. + */ +int __init usb_composite_register(struct usb_composite_driver *driver) +{ + if (!driver || !driver->dev || !driver->bind || composite) + return -EINVAL; + + if (!driver->name) + driver->name = "composite"; + composite_driver.function = (char *) driver->name; + composite_driver.driver.name = driver->name; + composite = driver; + + return usb_gadget_register_driver(&composite_driver); +} + +/** + * usb_composite_unregister() - unregister a composite driver + * @driver: the driver to unregister + * + * This function is used to unregister drivers using the composite + * driver framework. + */ +void __exit usb_composite_unregister(struct usb_composite_driver *driver) +{ + if (composite != driver) + return; + usb_gadget_unregister_driver(&composite_driver); +} diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h new file mode 100644 index 000000000000..747c3a49cdc9 --- /dev/null +++ b/include/linux/usb/composite.h @@ -0,0 +1,338 @@ +/* + * composite.h -- framework for usb gadgets which are composite devices + * + * Copyright (C) 2006-2008 David Brownell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __LINUX_USB_COMPOSITE_H +#define __LINUX_USB_COMPOSITE_H + +/* + * This framework is an optional layer on top of the USB Gadget interface, + * making it easier to build (a) Composite devices, supporting multiple + * functions within any single configuration, and (b) Multi-configuration + * devices, also supporting multiple functions but without necessarily + * having more than one function per configuration. + * + * Example: a device with a single configuration supporting both network + * link and mass storage functions is a composite device. Those functions + * might alternatively be packaged in individual configurations, but in + * the composite model the host can use both functions at the same time. + */ + +#include +#include + + +struct usb_configuration; + +/** + * struct usb_function - describes one function of a configuration + * @name: For diagnostics, identifies the function. + * @strings: tables of strings, keyed by identifiers assigned during bind() + * and by language IDs provided in control requests + * @descriptors: Table of full (or low) speed descriptors, using interface and + * string identifiers assigned during @bind(). If this pointer is null, + * the function will not be available at full speed (or at low speed). + * @hs_descriptors: Table of high speed descriptors, using interface and + * string identifiers assigned during @bind(). If this pointer is null, + * the function will not be available at high speed. + * @config: assigned when @usb_add_function() is called; this is the + * configuration with which this function is associated. + * @bind: Before the gadget can register, all of its functions bind() to the + * available resources including string and interface identifiers used + * in interface or class descriptors; endpoints; I/O buffers; and so on. + * @unbind: Reverses @bind; called as a side effect of unregistering the + * driver which added this function. + * @set_alt: (REQUIRED) Reconfigures altsettings; function drivers may + * initialize usb_ep.driver data at this time (when it is used). + * Note that setting an interface to its current altsetting resets + * interface state, and that all interfaces have a disabled state. + * @get_alt: Returns the active altsetting. If this is not provided, + * then only altsetting zero is supported. + * @disable: (REQUIRED) Indicates the function should be disabled. Reasons + * include host resetting or reconfiguring the gadget, and disconnection. + * @setup: Used for interface-specific control requests. + * @suspend: Notifies functions when the host stops sending USB traffic. + * @resume: Notifies functions when the host restarts USB traffic. + * + * A single USB function uses one or more interfaces, and should in most + * cases support operation at both full and high speeds. Each function is + * associated by @usb_add_function() with a one configuration; that function + * causes @bind() to be called so resources can be allocated as part of + * setting up a gadget driver. Those resources include endpoints, which + * should be allocated using @usb_ep_autoconfig(). + * + * To support dual speed operation, a function driver provides descriptors + * for both high and full speed operation. Except in rare cases that don't + * involve bulk endpoints, each speed needs different endpoint descriptors. + * + * Function drivers choose their own strategies for managing instance data. + * The simplest strategy just declares it "static', which means the function + * can only be activated once. If the function needs to be exposed in more + * than one configuration at a given speed, it needs to support multiple + * usb_function structures (one for each configuration). + * + * A more complex strategy might encapsulate a @usb_function structure inside + * a driver-specific instance structure to allows multiple activations. An + * example of multiple activations might be a CDC ACM function that supports + * two or more distinct instances within the same configuration, providing + * several independent logical data links to a USB host. + */ +struct usb_function { + const char *name; + struct usb_gadget_strings **strings; + struct usb_descriptor_header **descriptors; + struct usb_descriptor_header **hs_descriptors; + + struct usb_configuration *config; + + /* REVISIT: bind() functions can be marked __init, which + * makes trouble for section mismatch analysis. See if + * we can't restructure things to avoid mismatching. + * Related: unbind() may kfree() but bind() won't... + */ + + /* configuration management: bind/unbind */ + int (*bind)(struct usb_configuration *, + struct usb_function *); + void (*unbind)(struct usb_configuration *, + struct usb_function *); + + /* runtime state management */ + int (*set_alt)(struct usb_function *, + unsigned interface, unsigned alt); + int (*get_alt)(struct usb_function *, + unsigned interface); + void (*disable)(struct usb_function *); + int (*setup)(struct usb_function *, + const struct usb_ctrlrequest *); + void (*suspend)(struct usb_function *); + void (*resume)(struct usb_function *); + + /* internals */ + struct list_head list; +}; + +int usb_add_function(struct usb_configuration *, struct usb_function *); + +int usb_interface_id(struct usb_configuration *, struct usb_function *); + +/** + * ep_choose - select descriptor endpoint at current device speed + * @g: gadget, connected and running at some speed + * @hs: descriptor to use for high speed operation + * @fs: descriptor to use for full or low speed operation + */ +static inline struct usb_endpoint_descriptor * +ep_choose(struct usb_gadget *g, struct usb_endpoint_descriptor *hs, + struct usb_endpoint_descriptor *fs) +{ + if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + return hs; + return fs; +} + +#define MAX_CONFIG_INTERFACES 16 /* arbitrary; max 255 */ + +/** + * struct usb_configuration - represents one gadget configuration + * @label: For diagnostics, describes the configuration. + * @strings: Tables of strings, keyed by identifiers assigned during @bind() + * and by language IDs provided in control requests. + * @descriptors: Table of descriptors preceding all function descriptors. + * Examples include OTG and vendor-specific descriptors. + * @bind: Called from @usb_add_config() to allocate resources unique to this + * configuration and to call @usb_add_function() for each function used. + * @unbind: Reverses @bind; called as a side effect of unregistering the + * driver which added this configuration. + * @setup: Used to delegate control requests that aren't handled by standard + * device infrastructure or directed at a specific interface. + * @bConfigurationValue: Copied into configuration descriptor. + * @iConfiguration: Copied into configuration descriptor. + * @bmAttributes: Copied into configuration descriptor. + * @bMaxPower: Copied into configuration descriptor. + * @cdev: assigned by @usb_add_config() before calling @bind(); this is + * the device associated with this configuration. + * + * Configurations are building blocks for gadget drivers structured around + * function drivers. Simple USB gadgets require only one function and one + * configuration, and handle dual-speed hardware by always providing the same + * functionality. Slightly more complex gadgets may have more than one + * single-function configuration at a given speed; or have configurations + * that only work at one speed. + * + * Composite devices are, by definition, ones with configurations which + * include more than one function. + * + * The lifecycle of a usb_configuration includes allocation, initialization + * of the fields described above, and calling @usb_add_config() to set up + * internal data and bind it to a specific device. The configuration's + * @bind() method is then used to initialize all the functions and then + * call @usb_add_function() for them. + * + * Those functions would normally be independant of each other, but that's + * not mandatory. CDC WMC devices are an example where functions often + * depend on other functions, with some functions subsidiary to others. + * Such interdependency may be managed in any way, so long as all of the + * descriptors complete by the time the composite driver returns from + * its bind() routine. + */ +struct usb_configuration { + const char *label; + struct usb_gadget_strings **strings; + const struct usb_descriptor_header **descriptors; + + /* REVISIT: bind() functions can be marked __init, which + * makes trouble for section mismatch analysis. See if + * we can't restructure things to avoid mismatching... + */ + + /* configuration management: bind/unbind */ + int (*bind)(struct usb_configuration *); + void (*unbind)(struct usb_configuration *); + int (*setup)(struct usb_configuration *, + const struct usb_ctrlrequest *); + + /* fields in the config descriptor */ + u8 bConfigurationValue; + u8 iConfiguration; + u8 bmAttributes; + u8 bMaxPower; + + struct usb_composite_dev *cdev; + + /* internals */ + struct list_head list; + struct list_head functions; + u8 next_interface_id; + unsigned highspeed:1; + unsigned fullspeed:1; + struct usb_function *interface[MAX_CONFIG_INTERFACES]; +}; + +int usb_add_config(struct usb_composite_dev *, + struct usb_configuration *); + +/** + * struct usb_composite_driver - groups configurations into a gadget + * @name: For diagnostics, identifies the driver. + * @dev: Template descriptor for the device, including default device + * identifiers. + * @strings: tables of strings, keyed by identifiers assigned during bind() + * and language IDs provided in control requests + * @bind: (REQUIRED) Used to allocate resources that are shared across the + * whole device, such as string IDs, and add its configurations using + * @usb_add_config(). This may fail by returning a negative errno + * value; it should return zero on successful initialization. + * @unbind: Reverses @bind(); called as a side effect of unregistering + * this driver. + * + * Devices default to reporting self powered operation. Devices which rely + * on bus powered operation should report this in their @bind() method. + * + * Before returning from @bind, various fields in the template descriptor + * may be overridden. These include the idVendor/idProduct/bcdDevice values + * normally to bind the appropriate host side driver, and the three strings + * (iManufacturer, iProduct, iSerialNumber) normally used to provide user + * meaningful device identifiers. (The strings will not be defined unless + * they are defined in @dev and @strings.) The correct ep0 maxpacket size + * is also reported, as defined by the underlying controller driver. + */ +struct usb_composite_driver { + const char *name; + const struct usb_device_descriptor *dev; + struct usb_gadget_strings **strings; + + /* REVISIT: bind() functions can be marked __init, which + * makes trouble for section mismatch analysis. See if + * we can't restructure things to avoid mismatching... + */ + + int (*bind)(struct usb_composite_dev *); + int (*unbind)(struct usb_composite_dev *); +}; + +extern int usb_composite_register(struct usb_composite_driver *); +extern void usb_composite_unregister(struct usb_composite_driver *); + + +/** + * struct usb_composite_device - represents one composite usb gadget + * @gadget: read-only, abstracts the gadget's usb peripheral controller + * @req: used for control responses; buffer is pre-allocated + * @bufsiz: size of buffer pre-allocated in @req + * @config: the currently active configuration + * + * One of these devices is allocated and initialized before the + * associated device driver's bind() is called. + * + * OPEN ISSUE: it appears that some WUSB devices will need to be + * built by combining a normal (wired) gadget with a wireless one. + * This revision of the gadget framework should probably try to make + * sure doing that won't hurt too much. + * + * One notion for how to handle Wireless USB devices involves: + * (a) a second gadget here, discovery mechanism TBD, but likely + * needing separate "register/unregister WUSB gadget" calls; + * (b) updates to usb_gadget to include flags "is it wireless", + * "is it wired", plus (presumably in a wrapper structure) + * bandgroup and PHY info; + * (c) presumably a wireless_ep wrapping a usb_ep, and reporting + * wireless-specific parameters like maxburst and maxsequence; + * (d) configurations that are specific to wireless links; + * (e) function drivers that understand wireless configs and will + * support wireless for (additional) function instances; + * (f) a function to support association setup (like CBAF), not + * necessarily requiring a wireless adapter; + * (g) composite device setup that can create one or more wireless + * configs, including appropriate association setup support; + * (h) more, TBD. + */ +struct usb_composite_dev { + struct usb_gadget *gadget; + struct usb_request *req; + unsigned bufsiz; + + struct usb_configuration *config; + + /* internals */ + struct usb_device_descriptor desc; + struct list_head configs; + struct usb_composite_driver *driver; + u8 next_string_id; + + spinlock_t lock; + + /* REVISIT use and existence of lock ... */ +}; + +extern int usb_string_id(struct usb_composite_dev *c); + +/* messaging utils */ +#define DBG(d, fmt, args...) \ + dev_dbg(&(d)->gadget->dev , fmt , ## args) +#define VDBG(d, fmt, args...) \ + dev_vdbg(&(d)->gadget->dev , fmt , ## args) +#define ERROR(d, fmt, args...) \ + dev_err(&(d)->gadget->dev , fmt , ## args) +#define WARN(d, fmt, args...) \ + dev_warn(&(d)->gadget->dev , fmt , ## args) +#define INFO(d, fmt, args...) \ + dev_info(&(d)->gadget->dev , fmt , ## args) + +#endif /* __LINUX_USB_COMPOSITE_H */ -- cgit v1.2.3 From e0d795e4f36c2d8949c1355b497fc5425dbb9437 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 3 Jun 2008 14:47:52 +0300 Subject: usb: irda: cleanup on ir-usb module General cleanup on ir-usb module. Introduced a common header that could be used also on usb gadget framework. Lot's of cleanups and now using macros from the header file. Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ir-usb.c | 490 ++++++++++++++++++++++++-------------------- include/linux/usb/irda.h | 151 ++++++++++++++ 2 files changed, 421 insertions(+), 220 deletions(-) create mode 100644 include/linux/usb/irda.h (limited to 'include/linux') diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index 004d57385a75..0063c11c8081 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -19,7 +19,12 @@ * was written by Roman Weissgaerber , Dag Brattli * , and Jean Tourrilhes * - * See Documentation/usb/usb-serial.txt for more information on using this driver + * See Documentation/usb/usb-serial.txt for more information on using this + * driver + * + * 2008_Jun_02 Felipe Balbi + * Introduced common header to be used also in USB Gadget Framework. + * Still needs some other style fixes. * * 2007_Jun_21 Alan Cox * Minimal cleanups for some of the driver problens and tty layer abuse. @@ -59,9 +64,10 @@ #include #include #include -#include +#include #include #include +#include /* * Version Information @@ -70,100 +76,75 @@ #define DRIVER_AUTHOR "Greg Kroah-Hartman " #define DRIVER_DESC "USB IR Dongle driver" -/* USB IrDA class spec information */ -#define USB_CLASS_IRDA 0x02 -#define USB_DT_IRDA 0x21 -#define IU_REQ_GET_CLASS_DESC 0x06 -#define SPEED_2400 0x01 -#define SPEED_9600 0x02 -#define SPEED_19200 0x03 -#define SPEED_38400 0x04 -#define SPEED_57600 0x05 -#define SPEED_115200 0x06 -#define SPEED_576000 0x07 -#define SPEED_1152000 0x08 -#define SPEED_4000000 0x09 - -struct irda_class_desc { - u8 bLength; - u8 bDescriptorType; - u16 bcdSpecRevision; - u8 bmDataSize; - u8 bmWindowSize; - u8 bmMinTurnaroundTime; - u16 wBaudRate; - u8 bmAdditionalBOFs; - u8 bIrdaRateSniff; - u8 bMaxUnicastList; -} __attribute__ ((packed)); - static int debug; /* if overridden by the user, then use their value for the size of the read and * write urbs */ static int buffer_size; + /* if overridden by the user, then use the specified number of XBOFs */ static int xbof = -1; -static int ir_startup (struct usb_serial *serial); -static int ir_open (struct usb_serial_port *port, struct file *filep); -static void ir_close (struct usb_serial_port *port, struct file *filep); -static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int count); -static void ir_write_bulk_callback (struct urb *urb); -static void ir_read_bulk_callback (struct urb *urb); -static void ir_set_termios (struct usb_serial_port *port, struct ktermios *old_termios); +static int ir_startup(struct usb_serial *serial); +static int ir_open(struct usb_serial_port *port, struct file *filep); +static void ir_close(struct usb_serial_port *port, struct file *filep); +static int ir_write(struct usb_serial_port *port, + const unsigned char *buf, int count); +static void ir_write_bulk_callback(struct urb *urb); +static void ir_read_bulk_callback(struct urb *urb); +static void ir_set_termios(struct usb_serial_port *port, + struct ktermios *old_termios); /* Not that this lot means you can only have one per system */ -static u8 ir_baud = 0; -static u8 ir_xbof = 0; -static u8 ir_add_bof = 0; +static u8 ir_baud; +static u8 ir_xbof; +static u8 ir_add_bof; -static struct usb_device_id id_table [] = { +static struct usb_device_id ir_id_table[] = { { USB_DEVICE(0x050f, 0x0180) }, /* KC Technology, KC-180 */ { USB_DEVICE(0x08e9, 0x0100) }, /* XTNDAccess */ { USB_DEVICE(0x09c4, 0x0011) }, /* ACTiSys ACT-IR2000U */ - { USB_INTERFACE_INFO (USB_CLASS_APP_SPEC, USB_CLASS_IRDA, 0) }, + { USB_INTERFACE_INFO(USB_CLASS_APP_SPEC, USB_SUBCLASS_IRDA, 0) }, { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, id_table); +MODULE_DEVICE_TABLE(usb, ir_id_table); static struct usb_driver ir_driver = { - .name = "ir-usb", - .probe = usb_serial_probe, - .disconnect = usb_serial_disconnect, - .id_table = id_table, - .no_dynamic_id = 1, + .name = "ir-usb", + .probe = usb_serial_probe, + .disconnect = usb_serial_disconnect, + .id_table = ir_id_table, + .no_dynamic_id = 1, }; - static struct usb_serial_driver ir_device = { - .driver = { - .owner = THIS_MODULE, - .name = "ir-usb", + .driver = { + .owner = THIS_MODULE, + .name = "ir-usb", }, - .description = "IR Dongle", - .usb_driver = &ir_driver, - .id_table = id_table, - .num_ports = 1, - .set_termios = ir_set_termios, - .attach = ir_startup, - .open = ir_open, - .close = ir_close, - .write = ir_write, - .write_bulk_callback = ir_write_bulk_callback, - .read_bulk_callback = ir_read_bulk_callback, + .description = "IR Dongle", + .usb_driver = &ir_driver, + .id_table = ir_id_table, + .num_ports = 1, + .set_termios = ir_set_termios, + .attach = ir_startup, + .open = ir_open, + .close = ir_close, + .write = ir_write, + .write_bulk_callback = ir_write_bulk_callback, + .read_bulk_callback = ir_read_bulk_callback, }; -static inline void irda_usb_dump_class_desc(struct irda_class_desc *desc) +static inline void irda_usb_dump_class_desc(struct usb_irda_cs_descriptor *desc) { dbg("bLength=%x", desc->bLength); dbg("bDescriptorType=%x", desc->bDescriptorType); - dbg("bcdSpecRevision=%x", desc->bcdSpecRevision); + dbg("bcdSpecRevision=%x", __le16_to_cpu(desc->bcdSpecRevision)); dbg("bmDataSize=%x", desc->bmDataSize); dbg("bmWindowSize=%x", desc->bmWindowSize); dbg("bmMinTurnaroundTime=%d", desc->bmMinTurnaroundTime); - dbg("wBaudRate=%x", desc->wBaudRate); + dbg("wBaudRate=%x", __le16_to_cpu(desc->wBaudRate)); dbg("bmAdditionalBOFs=%x", desc->bmAdditionalBOFs); dbg("bIrdaRateSniff=%x", desc->bIrdaRateSniff); dbg("bMaxUnicastList=%x", desc->bMaxUnicastList); @@ -181,35 +162,37 @@ static inline void irda_usb_dump_class_desc(struct irda_class_desc *desc) * * Based on the same function in drivers/net/irda/irda-usb.c */ -static struct irda_class_desc *irda_usb_find_class_desc(struct usb_device *dev, unsigned int ifnum) +static struct usb_irda_cs_descriptor * +irda_usb_find_class_desc(struct usb_device *dev, unsigned int ifnum) { - struct irda_class_desc *desc; + struct usb_irda_cs_descriptor *desc; int ret; - - desc = kzalloc(sizeof (struct irda_class_desc), GFP_KERNEL); - if (desc == NULL) + + desc = kzalloc(sizeof(*desc), GFP_KERNEL); + if (!desc) return NULL; - - ret = usb_control_msg(dev, usb_rcvctrlpipe(dev,0), - IU_REQ_GET_CLASS_DESC, + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_CS_IRDA_GET_CLASS_DESC, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, ifnum, desc, sizeof(*desc), 1000); - + dbg("%s - ret=%d", __func__, ret); if (ret < sizeof(*desc)) { dbg("%s - class descriptor read %s (%d)", __func__, - (ret<0) ? "failed" : "too short", + (ret < 0) ? "failed" : "too short", ret); goto error; } - if (desc->bDescriptorType != USB_DT_IRDA) { + if (desc->bDescriptorType != USB_DT_CS_IRDA) { dbg("%s - bad class descriptor type", __func__); goto error; } - + irda_usb_dump_class_desc(desc); return desc; + error: kfree(desc); return NULL; @@ -219,64 +202,100 @@ error: static u8 ir_xbof_change(u8 xbof) { u8 result; + /* reference irda-usb.c */ - switch(xbof) { - case 48: result = 0x10; break; - case 28: - case 24: result = 0x20; break; - default: - case 12: result = 0x30; break; - case 5: - case 6: result = 0x40; break; - case 3: result = 0x50; break; - case 2: result = 0x60; break; - case 1: result = 0x70; break; - case 0: result = 0x80; break; + switch (xbof) { + case 48: + result = 0x10; + break; + case 28: + case 24: + result = 0x20; + break; + default: + case 12: + result = 0x30; + break; + case 5: + case 6: + result = 0x40; + break; + case 3: + result = 0x50; + break; + case 2: + result = 0x60; + break; + case 1: + result = 0x70; + break; + case 0: + result = 0x80; + break; } + return(result); } -static int ir_startup (struct usb_serial *serial) +static int ir_startup(struct usb_serial *serial) { - struct irda_class_desc *irda_desc; + struct usb_irda_cs_descriptor *irda_desc; - irda_desc = irda_usb_find_class_desc (serial->dev, 0); - if (irda_desc == NULL) { - dev_err (&serial->dev->dev, "IRDA class descriptor not found, device not bound\n"); + irda_desc = irda_usb_find_class_desc(serial->dev, 0); + if (!irda_desc) { + dev_err(&serial->dev->dev, + "IRDA class descriptor not found, device not bound\n"); return -ENODEV; } - dbg ("%s - Baud rates supported:%s%s%s%s%s%s%s%s%s", + dbg("%s - Baud rates supported:%s%s%s%s%s%s%s%s%s", __func__, - (irda_desc->wBaudRate & 0x0001) ? " 2400" : "", - (irda_desc->wBaudRate & 0x0002) ? " 9600" : "", - (irda_desc->wBaudRate & 0x0004) ? " 19200" : "", - (irda_desc->wBaudRate & 0x0008) ? " 38400" : "", - (irda_desc->wBaudRate & 0x0010) ? " 57600" : "", - (irda_desc->wBaudRate & 0x0020) ? " 115200" : "", - (irda_desc->wBaudRate & 0x0040) ? " 576000" : "", - (irda_desc->wBaudRate & 0x0080) ? " 1152000" : "", - (irda_desc->wBaudRate & 0x0100) ? " 4000000" : ""); - - switch( irda_desc->bmAdditionalBOFs ) { - case 0x01: ir_add_bof = 48; break; - case 0x02: ir_add_bof = 24; break; - case 0x04: ir_add_bof = 12; break; - case 0x08: ir_add_bof = 6; break; - case 0x10: ir_add_bof = 3; break; - case 0x20: ir_add_bof = 2; break; - case 0x40: ir_add_bof = 1; break; - case 0x80: ir_add_bof = 0; break; - default:; + (irda_desc->wBaudRate & USB_IRDA_BR_2400) ? " 2400" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_9600) ? " 9600" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_19200) ? " 19200" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_38400) ? " 38400" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_57600) ? " 57600" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_115200) ? " 115200" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_576000) ? " 576000" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_1152000) ? " 1152000" : "", + (irda_desc->wBaudRate & USB_IRDA_BR_4000000) ? " 4000000" : ""); + + switch (irda_desc->bmAdditionalBOFs) { + case USB_IRDA_AB_48: + ir_add_bof = 48; + break; + case USB_IRDA_AB_24: + ir_add_bof = 24; + break; + case USB_IRDA_AB_12: + ir_add_bof = 12; + break; + case USB_IRDA_AB_6: + ir_add_bof = 6; + break; + case USB_IRDA_AB_3: + ir_add_bof = 3; + break; + case USB_IRDA_AB_2: + ir_add_bof = 2; + break; + case USB_IRDA_AB_1: + ir_add_bof = 1; + break; + case USB_IRDA_AB_0: + ir_add_bof = 0; + break; + default: + break; } - kfree (irda_desc); + kfree(irda_desc); - return 0; + return 0; } -static int ir_open (struct usb_serial_port *port, struct file *filp) +static int ir_open(struct usb_serial_port *port, struct file *filp) { char *buffer; int result = 0; @@ -285,51 +304,55 @@ static int ir_open (struct usb_serial_port *port, struct file *filp) if (buffer_size) { /* override the default buffer sizes */ - buffer = kmalloc (buffer_size, GFP_KERNEL); + buffer = kmalloc(buffer_size, GFP_KERNEL); if (!buffer) { - dev_err (&port->dev, "%s - out of memory.\n", __func__); + dev_err(&port->dev, "%s - out of memory.\n", __func__); return -ENOMEM; } - kfree (port->read_urb->transfer_buffer); + kfree(port->read_urb->transfer_buffer); port->read_urb->transfer_buffer = buffer; port->read_urb->transfer_buffer_length = buffer_size; - buffer = kmalloc (buffer_size, GFP_KERNEL); + buffer = kmalloc(buffer_size, GFP_KERNEL); if (!buffer) { - dev_err (&port->dev, "%s - out of memory.\n", __func__); + dev_err(&port->dev, "%s - out of memory.\n", __func__); return -ENOMEM; } - kfree (port->write_urb->transfer_buffer); + kfree(port->write_urb->transfer_buffer); port->write_urb->transfer_buffer = buffer; port->write_urb->transfer_buffer_length = buffer_size; port->bulk_out_size = buffer_size; } /* Start reading from the device */ - usb_fill_bulk_urb ( + usb_fill_bulk_urb( port->read_urb, - port->serial->dev, - usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress), + port->serial->dev, + usb_rcvbulkpipe(port->serial->dev, + port->bulk_in_endpointAddress), port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, ir_read_bulk_callback, port); result = usb_submit_urb(port->read_urb, GFP_KERNEL); if (result) - dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", __func__, result); + dev_err(&port->dev, + "%s - failed submitting read urb, error %d\n", + __func__, result); return result; } -static void ir_close (struct usb_serial_port *port, struct file * filp) +static void ir_close(struct usb_serial_port *port, struct file *filp) { dbg("%s - port %d", __func__, port->number); - + /* shutdown our bulk read */ usb_kill_urb(port->read_urb); } -static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int count) +static int ir_write(struct usb_serial_port *port, + const unsigned char *buf, int count) { unsigned char *transfer_buffer; int result; @@ -338,7 +361,7 @@ static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int dbg("%s - port = %d, count = %d", __func__, port->number, count); if (!port->tty) { - dev_err (&port->dev, "%s - no tty???\n", __func__); + dev_err(&port->dev, "%s - no tty???\n", __func__); return 0; } @@ -359,7 +382,7 @@ static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int /* * The first byte of the packet we send to the device contains an - * inband header which indicates an additional number of BOFs and + * inbound header which indicates an additional number of BOFs and * a baud rate change. * * See section 5.4.2.2 of the USB IrDA spec. @@ -367,9 +390,9 @@ static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int *transfer_buffer = ir_xbof | ir_baud; ++transfer_buffer; - memcpy (transfer_buffer, buf, transfer_size); + memcpy(transfer_buffer, buf, transfer_size); - usb_fill_bulk_urb ( + usb_fill_bulk_urb( port->write_urb, port->serial->dev, usb_sndbulkpipe(port->serial->dev, @@ -381,17 +404,19 @@ static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int port->write_urb->transfer_flags = URB_ZERO_PACKET; - result = usb_submit_urb (port->write_urb, GFP_ATOMIC); + result = usb_submit_urb(port->write_urb, GFP_ATOMIC); if (result) { port->write_urb_busy = 0; - dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __func__, result); + dev_err(&port->dev, + "%s - failed submitting write urb, error %d\n", + __func__, result); } else result = transfer_size; return result; } -static void ir_write_bulk_callback (struct urb *urb) +static void ir_write_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; int status = urb->status; @@ -405,7 +430,7 @@ static void ir_write_bulk_callback (struct urb *urb) return; } - usb_serial_debug_data ( + usb_serial_debug_data( debug, &port->dev, __func__, @@ -415,7 +440,7 @@ static void ir_write_bulk_callback (struct urb *urb) usb_serial_port_softint(port); } -static void ir_read_bulk_callback (struct urb *urb) +static void ir_read_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; struct tty_struct *tty; @@ -431,68 +456,69 @@ static void ir_read_bulk_callback (struct urb *urb) } switch (status) { - case 0: /* Successful */ - - /* - * The first byte of the packet we get from the device - * contains a busy indicator and baud rate change. - * See section 5.4.1.2 of the USB IrDA spec. - */ - if ((*data & 0x0f) > 0) - ir_baud = *data & 0x0f; - - usb_serial_debug_data ( - debug, - &port->dev, - __func__, - urb->actual_length, - data); - - tty = port->tty; - - if (tty_buffer_request_room(tty, urb->actual_length - 1)) { - tty_insert_flip_string(tty, data+1, urb->actual_length - 1); - tty_flip_buffer_push(tty); - } - - /* - * No break here. - * We want to resubmit the urb so we can read - * again. - */ - - case -EPROTO: /* taking inspiration from pl2303.c */ - - /* Continue trying to always read */ - usb_fill_bulk_urb ( - port->read_urb, - port->serial->dev, - usb_rcvbulkpipe(port->serial->dev, - port->bulk_in_endpointAddress), - port->read_urb->transfer_buffer, - port->read_urb->transfer_buffer_length, - ir_read_bulk_callback, - port); - - result = usb_submit_urb(port->read_urb, GFP_ATOMIC); - if (result) - dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", - __func__, result); - - break ; - - default: - dbg("%s - nonzero read bulk status received: %d", - __func__, - status); - break ; + case 0: /* Successful */ + + /* + * The first byte of the packet we get from the device + * contains a busy indicator and baud rate change. + * See section 5.4.1.2 of the USB IrDA spec. + */ + if ((*data & 0x0f) > 0) + ir_baud = *data & 0x0f; + + usb_serial_debug_data( + debug, + &port->dev, + __func__, + urb->actual_length, + data); + + tty = port->tty; + + if (tty_buffer_request_room(tty, urb->actual_length - 1)) { + tty_insert_flip_string(tty, data + 1, + urb->actual_length - 1); + tty_flip_buffer_push(tty); + } + /* + * No break here. + * We want to resubmit the urb so we can read + * again. + */ + + case -EPROTO: /* taking inspiration from pl2303.c */ + + /* Continue trying to always read */ + usb_fill_bulk_urb( + port->read_urb, + port->serial->dev, + usb_rcvbulkpipe(port->serial->dev, + port->bulk_in_endpointAddress), + port->read_urb->transfer_buffer, + port->read_urb->transfer_buffer_length, + ir_read_bulk_callback, + port); + + result = usb_submit_urb(port->read_urb, GFP_ATOMIC); + if (result) + dev_err(&port->dev, + "%s - failed resubmitting read urb, error %d\n", + __func__, result); + break; + + default: + dbg("%s - nonzero read bulk status received: %d", + __func__, + status); + break; } return; } -static void ir_set_termios (struct usb_serial_port *port, struct ktermios *old_termios) +static void ir_set_termios(struct usb_serial_port *port, + struct ktermios *old_termios) { unsigned char *transfer_buffer; int result; @@ -510,19 +536,36 @@ static void ir_set_termios (struct usb_serial_port *port, struct ktermios *old_t */ switch (baud) { - case 2400: ir_baud = SPEED_2400; break; - case 9600: ir_baud = SPEED_9600; break; - case 19200: ir_baud = SPEED_19200; break; - case 38400: ir_baud = SPEED_38400; break; - case 57600: ir_baud = SPEED_57600; break; - case 115200: ir_baud = SPEED_115200; break; - case 576000: ir_baud = SPEED_576000; break; - case 1152000: ir_baud = SPEED_1152000; break; - case 4000000: ir_baud = SPEED_4000000; break; - break; - default: - ir_baud = SPEED_9600; - baud = 9600; + case 2400: + ir_baud = USB_IRDA_BR_2400; + break; + case 9600: + ir_baud = USB_IRDA_BR_9600; + break; + case 19200: + ir_baud = USB_IRDA_BR_19200; + break; + case 38400: + ir_baud = USB_IRDA_BR_38400; + break; + case 57600: + ir_baud = USB_IRDA_BR_57600; + break; + case 115200: + ir_baud = USB_IRDA_BR_115200; + break; + case 576000: + ir_baud = USB_IRDA_BR_576000; + break; + case 1152000: + ir_baud = USB_IRDA_BR_1152000; + break; + case 4000000: + ir_baud = USB_IRDA_BR_4000000; + break; + default: + ir_baud = USB_IRDA_BR_9600; + baud = 9600; } if (xbof == -1) @@ -538,10 +581,11 @@ static void ir_set_termios (struct usb_serial_port *port, struct ktermios *old_t transfer_buffer = port->write_urb->transfer_buffer; *transfer_buffer = ir_xbof | ir_baud; - usb_fill_bulk_urb ( + usb_fill_bulk_urb( port->write_urb, port->serial->dev, - usb_sndbulkpipe(port->serial->dev, port->bulk_out_endpointAddress), + usb_sndbulkpipe(port->serial->dev, + port->bulk_out_endpointAddress), port->write_urb->transfer_buffer, 1, ir_write_bulk_callback, @@ -549,38 +593,44 @@ static void ir_set_termios (struct usb_serial_port *port, struct ktermios *old_t port->write_urb->transfer_flags = URB_ZERO_PACKET; - result = usb_submit_urb (port->write_urb, GFP_KERNEL); + result = usb_submit_urb(port->write_urb, GFP_KERNEL); if (result) - dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __func__, result); + dev_err(&port->dev, + "%s - failed submitting write urb, error %d\n", + __func__, result); /* Only speed changes are supported */ tty_termios_copy_hw(port->tty->termios, old_termios); tty_encode_baud_rate(port->tty, baud, baud); } - -static int __init ir_init (void) +static int __init ir_init(void) { int retval; + retval = usb_serial_register(&ir_device); if (retval) goto failed_usb_serial_register; + retval = usb_register(&ir_driver); - if (retval) + if (retval) goto failed_usb_register; + info(DRIVER_DESC " " DRIVER_VERSION); + return 0; + failed_usb_register: usb_serial_deregister(&ir_device); + failed_usb_serial_register: return retval; } - -static void __exit ir_exit (void) +static void __exit ir_exit(void) { - usb_deregister (&ir_driver); - usb_serial_deregister (&ir_device); + usb_deregister(&ir_driver); + usb_serial_deregister(&ir_device); } diff --git a/include/linux/usb/irda.h b/include/linux/usb/irda.h new file mode 100644 index 000000000000..e345ceaf72d6 --- /dev/null +++ b/include/linux/usb/irda.h @@ -0,0 +1,151 @@ +/* + * USB IrDA Bridge Device Definition + */ + +#ifndef __LINUX_USB_IRDA_H +#define __LINUX_USB_IRDA_H + +/* This device should use Application-specific class */ + +#define USB_SUBCLASS_IRDA 0x02 + +/*-------------------------------------------------------------------------*/ + +/* Class-Specific requests (bRequest field) */ + +#define USB_REQ_CS_IRDA_RECEIVING 1 +#define USB_REQ_CS_IRDA_CHECK_MEDIA_BUSY 3 +#define USB_REQ_CS_IRDA_RATE_SNIFF 4 +#define USB_REQ_CS_IRDA_UNICAST_LIST 5 +#define USB_REQ_CS_IRDA_GET_CLASS_DESC 6 + +/*-------------------------------------------------------------------------*/ + +/* Class-Specific descriptor */ + +#define USB_DT_CS_IRDA 0x21 + +/*-------------------------------------------------------------------------*/ + +/* Data sizes */ + +#define USB_IRDA_DS_2048 (1 << 5) +#define USB_IRDA_DS_1024 (1 << 4) +#define USB_IRDA_DS_512 (1 << 3) +#define USB_IRDA_DS_256 (1 << 2) +#define USB_IRDA_DS_128 (1 << 1) +#define USB_IRDA_DS_64 (1 << 0) + +/* Window sizes */ + +#define USB_IRDA_WS_7 (1 << 6) +#define USB_IRDA_WS_6 (1 << 5) +#define USB_IRDA_WS_5 (1 << 4) +#define USB_IRDA_WS_4 (1 << 3) +#define USB_IRDA_WS_3 (1 << 2) +#define USB_IRDA_WS_2 (1 << 1) +#define USB_IRDA_WS_1 (1 << 0) + +/* Min turnaround times in usecs */ + +#define USB_IRDA_MTT_0 (1 << 7) +#define USB_IRDA_MTT_10 (1 << 6) +#define USB_IRDA_MTT_50 (1 << 5) +#define USB_IRDA_MTT_100 (1 << 4) +#define USB_IRDA_MTT_500 (1 << 3) +#define USB_IRDA_MTT_1000 (1 << 2) +#define USB_IRDA_MTT_5000 (1 << 1) +#define USB_IRDA_MTT_10000 (1 << 0) + +/* Baud rates */ + +#define USB_IRDA_BR_4000000 (1 << 8) +#define USB_IRDA_BR_1152000 (1 << 7) +#define USB_IRDA_BR_576000 (1 << 6) +#define USB_IRDA_BR_115200 (1 << 5) +#define USB_IRDA_BR_57600 (1 << 4) +#define USB_IRDA_BR_38400 (1 << 3) +#define USB_IRDA_BR_19200 (1 << 2) +#define USB_IRDA_BR_9600 (1 << 1) +#define USB_IRDA_BR_2400 (1 << 0) + +/* Additional BOFs */ + +#define USB_IRDA_AB_0 (1 << 7) +#define USB_IRDA_AB_1 (1 << 6) +#define USB_IRDA_AB_2 (1 << 5) +#define USB_IRDA_AB_3 (1 << 4) +#define USB_IRDA_AB_6 (1 << 3) +#define USB_IRDA_AB_12 (1 << 2) +#define USB_IRDA_AB_24 (1 << 1) +#define USB_IRDA_AB_48 (1 << 0) + +/* IRDA Rate Sniff */ + +#define USB_IRDA_RATE_SNIFF 1 + +/*-------------------------------------------------------------------------*/ + +struct usb_irda_cs_descriptor { + __u8 bLength; + __u8 bDescriptorType; + + __le16 bcdSpecRevision; + __u8 bmDataSize; + __u8 bmWindowSize; + __u8 bmMinTurnaroundTime; + __le16 wBaudRate; + __u8 bmAdditionalBOFs; + __u8 bIrdaRateSniff; + __u8 bMaxUnicastList; +} __attribute__ ((packed)); + +/*-------------------------------------------------------------------------*/ + +/* Data Format */ + +#define USB_IRDA_STATUS_MEDIA_BUSY (1 << 7) + +/* The following is a 4-bit value used for both + * inbound and outbound headers: + * + * 0 - speed ignored + * 1 - 2400 bps + * 2 - 9600 bps + * 3 - 19200 bps + * 4 - 38400 bps + * 5 - 57600 bps + * 6 - 115200 bps + * 7 - 576000 bps + * 8 - 1.152 Mbps + * 9 - 5 mbps + * 10..15 - Reserved + */ +#define USB_IRDA_STATUS_LINK_SPEED 0x0f + +/* The following is a 4-bit value used only for + * outbound header: + * + * 0 - No change (BOF ignored) + * 1 - 48 BOFs + * 2 - 24 BOFs + * 3 - 12 BOFs + * 4 - 6 BOFs + * 5 - 3 BOFs + * 6 - 2 BOFs + * 7 - 1 BOFs + * 8 - 0 BOFs + * 9..15 - Reserved + */ +#define USB_IRDA_EXTRA_BOFS 0xf0 + +struct usb_irda_inbound_header { + __u8 bmStatus; +}; + +struct usb_irda_outbound_header { + __u8 bmChange; +}; + +#endif /* __LINUX_USB_IRDA_H */ + -- cgit v1.2.3 From f579c2b46f74038e8f5a762c7f10c2385b33e3dc Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 2 Jun 2008 16:26:48 -0400 Subject: USB Gadget: documentation update This patch (as1102) clarifies two points in the USB Gadget kerneldoc: Request completion callbacks are always made with interrupts disabled; Device controllers may not support STALLing the status stage of a control transfer after the data stage is over. Signed-off-by: Alan Stern Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/gadget.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 0ebedaec075d..0460a746480c 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -33,7 +33,8 @@ struct usb_ep; * @short_not_ok: When reading data, makes short packets be * treated as errors (queue stops advancing till cleanup). * @complete: Function called when request completes, so this request and - * its buffer may be re-used. + * its buffer may be re-used. The function will always be called with + * interrupts disabled, and it must not sleep. * Reads terminate with a short packet, or when the buffer fills, * whichever comes first. When writes terminate, some data bytes * will usually still be in flight (often in a hardware fifo). @@ -271,7 +272,10 @@ static inline void usb_ep_free_request(struct usb_ep *ep, * (Note that some USB device controllers disallow protocol stall responses * in some cases.) When control responses are deferred (the response is * written after the setup callback returns), then usb_ep_set_halt() may be - * used on ep0 to trigger protocol stalls. + * used on ep0 to trigger protocol stalls. Depending on the controller, + * it may not be possible to trigger a status-stage protocol stall when the + * data stage is over, that is, from within the response's completion + * routine. * * For periodic endpoints, like interrupt or isochronous ones, the usb host * arranges to poll once per interval, and the gadget driver usually will -- cgit v1.2.3 From 625f694936cbbdee98e6cc65f72724a7660e7946 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Sun, 15 Jun 2008 09:42:02 +0800 Subject: USB: remove interface parameter of usb_reset_composite_device From the current implementation of usb_reset_composite_device function, the iface parameter is no longer useful. This function doesn't do something special for the iface usb_interface,compared with other interfaces in the usb_device. So remove the parameter and fix the related caller. Signed-off-by: Ming Lei Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/hid/usbhid/hid-core.c | 2 +- drivers/usb/core/devio.c | 2 +- drivers/usb/core/hub.c | 11 +++-------- drivers/usb/storage/transport.c | 3 +-- include/linux/usb.h | 3 +-- 5 files changed, 7 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 01427c51c7cc..69fa79b6b51c 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -122,7 +122,7 @@ static void hid_reset(struct work_struct *work) dev_dbg(&usbhid->intf->dev, "resetting device\n"); rc = rc_lock = usb_lock_device_for_reset(hid_to_usb_dev(hid), usbhid->intf); if (rc_lock >= 0) { - rc = usb_reset_composite_device(hid_to_usb_dev(hid), usbhid->intf); + rc = usb_reset_composite_device(hid_to_usb_dev(hid)); if (rc_lock) usb_unlock_device(hid_to_usb_dev(hid)); } diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 7bee9c18b3bc..bc1cce5cf758 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -872,7 +872,7 @@ static int proc_connectinfo(struct dev_state *ps, void __user *arg) static int proc_resetdevice(struct dev_state *ps) { - return usb_reset_composite_device(ps->dev, NULL); + return usb_reset_composite_device(ps->dev); } static int proc_setintf(struct dev_state *ps, void __user *arg) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 3251120b414e..207c33d369be 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2712,7 +2712,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, #endif } else { - status = usb_reset_composite_device(udev, NULL); + status = usb_reset_composite_device(udev); } usb_unlock_device(udev); @@ -2940,7 +2940,7 @@ static void hub_events(void) dev_dbg (hub_dev, "resetting for error %d\n", hub->error); - ret = usb_reset_composite_device(hdev, intf); + ret = usb_reset_composite_device(hdev); if (ret) { dev_dbg (hub_dev, "error resetting hub: %d\n", ret); @@ -3355,7 +3355,6 @@ EXPORT_SYMBOL_GPL(usb_reset_device); /** * usb_reset_composite_device - warn interface drivers and perform a USB port reset * @udev: device to reset (not in SUSPENDED or NOTATTACHED state) - * @iface: interface bound to the driver making the request (optional) * * Warns all drivers bound to registered interfaces (using their pre_reset * method), performs the port reset, and then lets the drivers know that @@ -3368,8 +3367,7 @@ EXPORT_SYMBOL_GPL(usb_reset_device); * For calls that might not occur during probe(), drivers should lock * the device using usb_lock_device_for_reset(). */ -int usb_reset_composite_device(struct usb_device *udev, - struct usb_interface *iface) +int usb_reset_composite_device(struct usb_device *udev) { int ret; int i; @@ -3385,9 +3383,6 @@ int usb_reset_composite_device(struct usb_device *udev, /* Prevent autosuspend during the reset */ usb_autoresume_device(udev); - if (iface && iface->condition != USB_INTERFACE_BINDING) - iface = NULL; - if (config) { for (i = 0; i < config->desc.bNumInterfaces; ++i) { struct usb_interface *cintf = config->interface[i]; diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 08d3a13fec2c..670e4cbd1f06 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -1172,8 +1172,7 @@ int usb_stor_port_reset(struct us_data *us) result = -EIO; US_DEBUGP("No reset during disconnect\n"); } else { - result = usb_reset_composite_device( - us->pusb_dev, us->pusb_intf); + result = usb_reset_composite_device(us->pusb_dev); US_DEBUGP("usb_reset_composite_device returns %d\n", result); } diff --git a/include/linux/usb.h b/include/linux/usb.h index 8429d08bd2fd..c74cc64bddc8 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -497,8 +497,7 @@ extern int usb_lock_device_for_reset(struct usb_device *udev, /* USB port reset for device reinitialization */ extern int usb_reset_device(struct usb_device *dev); -extern int usb_reset_composite_device(struct usb_device *dev, - struct usb_interface *iface); +extern int usb_reset_composite_device(struct usb_device *dev); extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id); -- cgit v1.2.3 From 742120c63138651c898614001cb58cd607401eac Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Wed, 18 Jun 2008 22:00:29 +0800 Subject: USB: fix usb_reset_device and usb_reset_composite_device(take 3) This patch renames the existing usb_reset_device in hub.c to usb_reset_and_verify_device and renames the existing usb_reset_composite_device to usb_reset_device. Also the new usb_reset_and_verify_device does't need to be EXPORTED . The idea of the patch is that external interface driver should warn the other interfaces' driver of the same device before and after reseting the usb device. One interface driver shoud call _old_ usb_reset_composite_device instead of _old_ usb_reset_device since it can't assume the device contains only one interface. The _old_ usb_reset_composite_device is safe for single interface device also. we rename the two functions to make the change easily. This patch is under guideline from Alan Stern. Signed-off-by: Ming Lei --- drivers/hid/usbhid/hid-core.c | 2 +- drivers/usb/core/devio.c | 2 +- drivers/usb/core/hub.c | 33 +++++++++++++++++---------------- drivers/usb/storage/transport.c | 4 ++-- include/linux/usb.h | 5 ++--- 5 files changed, 23 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 69fa79b6b51c..27fe4d8912cb 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -122,7 +122,7 @@ static void hid_reset(struct work_struct *work) dev_dbg(&usbhid->intf->dev, "resetting device\n"); rc = rc_lock = usb_lock_device_for_reset(hid_to_usb_dev(hid), usbhid->intf); if (rc_lock >= 0) { - rc = usb_reset_composite_device(hid_to_usb_dev(hid)); + rc = usb_reset_device(hid_to_usb_dev(hid)); if (rc_lock) usb_unlock_device(hid_to_usb_dev(hid)); } diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index bc1cce5cf758..e09935acae80 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -872,7 +872,7 @@ static int proc_connectinfo(struct dev_state *ps, void __user *arg) static int proc_resetdevice(struct dev_state *ps) { - return usb_reset_composite_device(ps->dev); + return usb_reset_device(ps->dev); } static int proc_setintf(struct dev_state *ps, void __user *arg) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 207c33d369be..bb3ecc4c08f2 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -135,6 +135,8 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem); #define HUB_DEBOUNCE_STABLE 100 +static int usb_reset_and_verify_device(struct usb_device *udev); + static inline char *portspeed(int portstatus) { if (portstatus & (1 << USB_PORT_FEAT_HIGHSPEED)) @@ -1971,7 +1973,7 @@ static int finish_port_resume(struct usb_device *udev) * resumed. */ if (udev->reset_resume) - status = usb_reset_device(udev); + status = usb_reset_and_verify_device(udev); /* 10.5.4.5 says be sure devices in the tree are still there. * For now let's assume the device didn't go crazy on resume, @@ -2030,7 +2032,7 @@ static int finish_port_resume(struct usb_device *udev) * to it will be lost. Using the USB_PERSIST facility, the device can be * made to appear as if it had not disconnected. * - * This facility can be dangerous. Although usb_reset_device() makes + * This facility can be dangerous. Although usb_reset_and_verify_device() makes * every effort to insure that the same device is present after the * reset as before, it cannot provide a 100% guarantee. Furthermore it's * quite possible for a device to remain unaltered but its media to be @@ -2140,7 +2142,7 @@ int usb_port_resume(struct usb_device *udev) hub_port_logical_disconnect(hub, port1); } else if (udev->reset_resume) { dev_dbg(&udev->dev, "reset-resume\n"); - status = usb_reset_device(udev); + status = usb_reset_and_verify_device(udev); } return status; } @@ -2321,7 +2323,7 @@ static int hub_set_address(struct usb_device *udev, int devnum) * Returns device in USB_STATE_ADDRESS, except on error. * * If this is called for an already-existing device (as part of - * usb_reset_device), the caller must own the device lock. For a + * usb_reset_and_verify_device), the caller must own the device lock. For a * newly detected device that is not accessible through any global * pointers, it's not necessary to lock the device. */ @@ -2638,7 +2640,7 @@ hub_power_remaining (struct usb_hub *hub) * This routine is called when: * a port connection-change occurs; * a port enable-change occurs (often caused by EMI); - * usb_reset_device() encounters changed descriptors (as from + * usb_reset_and_verify_device() encounters changed descriptors (as from * a firmware download) * caller already locked the hub */ @@ -2712,7 +2714,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, #endif } else { - status = usb_reset_composite_device(udev); + status = usb_reset_device(udev); } usb_unlock_device(udev); @@ -2940,7 +2942,7 @@ static void hub_events(void) dev_dbg (hub_dev, "resetting for error %d\n", hub->error); - ret = usb_reset_composite_device(hdev); + ret = usb_reset_device(hdev); if (ret) { dev_dbg (hub_dev, "error resetting hub: %d\n", ret); @@ -3233,12 +3235,12 @@ static int descriptors_changed(struct usb_device *udev, } /** - * usb_reset_device - perform a USB port reset to reinitialize a device + * usb_reset_and_verify_device - perform a USB port reset to reinitialize a device * @udev: device to reset (not in SUSPENDED or NOTATTACHED state) * * WARNING - don't use this routine to reset a composite device * (one with multiple interfaces owned by separate drivers)! - * Use usb_reset_composite_device() instead. + * Use usb_reset_device() instead. * * Do a port reset, reassign the device's address, and establish its * former operating configuration. If the reset fails, or the device's @@ -3262,7 +3264,7 @@ static int descriptors_changed(struct usb_device *udev, * holding the device lock because these tasks should always call * usb_autopm_resume_device(), thereby preventing any unwanted autoresume. */ -int usb_reset_device(struct usb_device *udev) +static int usb_reset_and_verify_device(struct usb_device *udev) { struct usb_device *parent_hdev = udev->parent; struct usb_hub *parent_hub; @@ -3350,24 +3352,23 @@ re_enumerate: hub_port_logical_disconnect(parent_hub, port1); return -ENODEV; } -EXPORT_SYMBOL_GPL(usb_reset_device); /** - * usb_reset_composite_device - warn interface drivers and perform a USB port reset + * usb_reset_device - warn interface drivers and perform a USB port reset * @udev: device to reset (not in SUSPENDED or NOTATTACHED state) * * Warns all drivers bound to registered interfaces (using their pre_reset * method), performs the port reset, and then lets the drivers know that * the reset is over (using their post_reset method). * - * Return value is the same as for usb_reset_device(). + * Return value is the same as for usb_reset_and_verify_device(). * * The caller must own the device lock. For example, it's safe to use * this from a driver probe() routine after downloading new firmware. * For calls that might not occur during probe(), drivers should lock * the device using usb_lock_device_for_reset(). */ -int usb_reset_composite_device(struct usb_device *udev) +int usb_reset_device(struct usb_device *udev) { int ret; int i; @@ -3397,7 +3398,7 @@ int usb_reset_composite_device(struct usb_device *udev) } } - ret = usb_reset_device(udev); + ret = usb_reset_and_verify_device(udev); if (config) { for (i = config->desc.bNumInterfaces - 1; i >= 0; --i) { @@ -3416,4 +3417,4 @@ int usb_reset_composite_device(struct usb_device *udev) usb_autosuspend_device(udev); return ret; } -EXPORT_SYMBOL_GPL(usb_reset_composite_device); +EXPORT_SYMBOL_GPL(usb_reset_device); diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 670e4cbd1f06..fcbbfdb7b2b0 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -1172,8 +1172,8 @@ int usb_stor_port_reset(struct us_data *us) result = -EIO; US_DEBUGP("No reset during disconnect\n"); } else { - result = usb_reset_composite_device(us->pusb_dev); - US_DEBUGP("usb_reset_composite_device returns %d\n", + result = usb_reset_device(us->pusb_dev); + US_DEBUGP("usb_reset_device returns %d\n", result); } if (rc_lock) diff --git a/include/linux/usb.h b/include/linux/usb.h index c74cc64bddc8..3cc8db5254d1 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -497,7 +497,6 @@ extern int usb_lock_device_for_reset(struct usb_device *udev, /* USB port reset for device reinitialization */ extern int usb_reset_device(struct usb_device *dev); -extern int usb_reset_composite_device(struct usb_device *dev); extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id); @@ -957,9 +956,9 @@ struct usbdrv_wrap { * @resume: Called when the device is being resumed by the system. * @reset_resume: Called when the suspended device has been reset instead * of being resumed. - * @pre_reset: Called by usb_reset_composite_device() when the device + * @pre_reset: Called by usb_reset_device() when the device * is about to be reset. - * @post_reset: Called by usb_reset_composite_device() after the device + * @post_reset: Called by usb_reset_device() after the device * has been reset * @id_table: USB drivers use ID table to support hotplugging. * Export this with MODULE_DEVICE_TABLE(usb,...). This must be set -- cgit v1.2.3 From 78d9a487ee961c356e1a934d9a92eca38ffb3a70 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 23 Jun 2008 16:00:40 -0400 Subject: USB: Force unbinding of drivers lacking reset_resume or other methods This patch (as1024) takes care of a FIXME issue: Drivers that don't have the necessary suspend, resume, reset_resume, pre_reset, or post_reset methods will be unbound and their interface reprobed when one of the unsupported events occurs. This is made slightly more difficult by the fact that bind operations won't work during a system sleep transition. So instead the code has to defer the operation until the transition ends. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 131 ++++++++++++++++++++++++++++++++++++++++------ drivers/usb/core/hub.c | 27 +++++++--- drivers/usb/core/usb.h | 2 + include/linux/usb.h | 1 + 4 files changed, 140 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 8da1a56659be..ddb54e14a5c5 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -201,6 +201,7 @@ static int usb_probe_interface(struct device *dev) intf = to_usb_interface(dev); udev = interface_to_usbdev(intf); + intf->needs_binding = 0; if (udev->authorized == 0) { dev_err(&intf->dev, "Device is not authorized for usage\n"); @@ -311,6 +312,7 @@ int usb_driver_claim_interface(struct usb_driver *driver, dev->driver = &driver->drvwrap.driver; usb_set_intfdata(iface, priv); + iface->needs_binding = 0; usb_pm_lock(udev); iface->condition = USB_INTERFACE_BOUND; @@ -772,6 +774,104 @@ void usb_deregister(struct usb_driver *driver) } EXPORT_SYMBOL_GPL(usb_deregister); + +/* Forced unbinding of a USB interface driver, either because + * it doesn't support pre_reset/post_reset/reset_resume or + * because it doesn't support suspend/resume. + * + * The caller must hold @intf's device's lock, but not its pm_mutex + * and not @intf->dev.sem. + */ +void usb_forced_unbind_intf(struct usb_interface *intf) +{ + struct usb_driver *driver = to_usb_driver(intf->dev.driver); + + dev_dbg(&intf->dev, "forced unbind\n"); + usb_driver_release_interface(driver, intf); + + /* Mark the interface for later rebinding */ + intf->needs_binding = 1; +} + +/* Delayed forced unbinding of a USB interface driver and scan + * for rebinding. + * + * The caller must hold @intf's device's lock, but not its pm_mutex + * and not @intf->dev.sem. + * + * FIXME: The caller must block system sleep transitions. + */ +void usb_rebind_intf(struct usb_interface *intf) +{ + int rc; + + /* Delayed unbind of an existing driver */ + if (intf->dev.driver) { + struct usb_driver *driver = + to_usb_driver(intf->dev.driver); + + dev_dbg(&intf->dev, "forced unbind\n"); + usb_driver_release_interface(driver, intf); + } + + /* Try to rebind the interface */ + intf->needs_binding = 0; + rc = device_attach(&intf->dev); + if (rc < 0) + dev_warn(&intf->dev, "rebind failed: %d\n", rc); +} + +#define DO_UNBIND 0 +#define DO_REBIND 1 + +/* Unbind drivers for @udev's interfaces that don't support suspend/resume, + * or rebind interfaces that have been unbound, according to @action. + * + * The caller must hold @udev's device lock. + * FIXME: For rebinds, the caller must block system sleep transitions. + */ +static void do_unbind_rebind(struct usb_device *udev, int action) +{ + struct usb_host_config *config; + int i; + struct usb_interface *intf; + struct usb_driver *drv; + + config = udev->actconfig; + if (config) { + for (i = 0; i < config->desc.bNumInterfaces; ++i) { + intf = config->interface[i]; + switch (action) { + case DO_UNBIND: + if (intf->dev.driver) { + drv = to_usb_driver(intf->dev.driver); + if (!drv->suspend || !drv->resume) + usb_forced_unbind_intf(intf); + } + break; + case DO_REBIND: + if (intf->needs_binding) { + + /* FIXME: The next line is needed because we are going to probe + * the interface, but as far as the PM core is concerned the + * interface is still suspended. The problem wouldn't exist + * if we could rebind the interface during the interface's own + * resume() call, but at the time the usb_device isn't locked! + * + * The real solution will be to carry this out during the device's + * complete() callback. Until that is implemented, we have to + * use this hack. + */ +// intf->dev.power.sleeping = 0; + + usb_rebind_intf(intf); + } + break; + } + } + } +} + #ifdef CONFIG_PM /* Caller has locked udev's pm_mutex */ @@ -841,7 +941,7 @@ static int usb_suspend_interface(struct usb_interface *intf, pm_message_t msg) goto done; driver = to_usb_driver(intf->dev.driver); - if (driver->suspend && driver->resume) { + if (driver->suspend) { status = driver->suspend(intf, msg); if (status == 0) mark_quiesced(intf); @@ -849,12 +949,10 @@ static int usb_suspend_interface(struct usb_interface *intf, pm_message_t msg) dev_err(&intf->dev, "%s error %d\n", "suspend", status); } else { - /* - * FIXME else if there's no suspend method, disconnect... - * Not possible if auto_pm is set... - */ - dev_warn(&intf->dev, "no suspend for driver %s?\n", - driver->name); + /* Later we will unbind the driver and reprobe */ + intf->needs_binding = 1; + dev_warn(&intf->dev, "no %s for driver %s?\n", + "suspend", driver->name); mark_quiesced(intf); } @@ -878,10 +976,12 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume) goto done; /* Can't resume it if it doesn't have a driver. */ - if (intf->condition == USB_INTERFACE_UNBOUND) { - status = -ENOTCONN; + if (intf->condition == USB_INTERFACE_UNBOUND) + goto done; + + /* Don't resume if the interface is marked for rebinding */ + if (intf->needs_binding) goto done; - } driver = to_usb_driver(intf->dev.driver); if (reset_resume) { @@ -891,7 +991,7 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume) dev_err(&intf->dev, "%s error %d\n", "reset_resume", status); } else { - /* status = -EOPNOTSUPP; */ + intf->needs_binding = 1; dev_warn(&intf->dev, "no %s for driver %s?\n", "reset_resume", driver->name); } @@ -902,7 +1002,7 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume) dev_err(&intf->dev, "%s error %d\n", "resume", status); } else { - /* status = -EOPNOTSUPP; */ + intf->needs_binding = 1; dev_warn(&intf->dev, "no %s for driver %s?\n", "resume", driver->name); } @@ -910,11 +1010,10 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume) done: dev_vdbg(&intf->dev, "%s: status %d\n", __func__, status); - if (status == 0) + if (status == 0 && intf->condition == USB_INTERFACE_BOUND) mark_active(intf); - /* FIXME: Unbind the driver and reprobe if the resume failed - * (not possible if auto_pm is set) */ + /* Later we will unbind the driver and/or reprobe, if necessary */ return status; } @@ -1470,6 +1569,7 @@ int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg) { int status; + do_unbind_rebind(udev, DO_UNBIND); usb_pm_lock(udev); udev->auto_pm = 0; status = usb_suspend_both(udev, msg); @@ -1497,6 +1597,7 @@ int usb_external_resume_device(struct usb_device *udev) status = usb_resume_both(udev); udev->last_busy = jiffies; usb_pm_unlock(udev); + do_unbind_rebind(udev, DO_REBIND); /* Now that the device is awake, we can start trying to autosuspend * it again. */ diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index bb3ecc4c08f2..f1efabbc1ca2 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3367,6 +3367,11 @@ re_enumerate: * this from a driver probe() routine after downloading new firmware. * For calls that might not occur during probe(), drivers should lock * the device using usb_lock_device_for_reset(). + * + * If an interface is currently being probed or disconnected, we assume + * its driver knows how to handle resets. For all other interfaces, + * if the driver doesn't have pre_reset and post_reset methods then + * we attempt to unbind it and rebind afterward. */ int usb_reset_device(struct usb_device *udev) { @@ -3388,12 +3393,17 @@ int usb_reset_device(struct usb_device *udev) for (i = 0; i < config->desc.bNumInterfaces; ++i) { struct usb_interface *cintf = config->interface[i]; struct usb_driver *drv; + int unbind = 0; if (cintf->dev.driver) { drv = to_usb_driver(cintf->dev.driver); - if (drv->pre_reset) - (drv->pre_reset)(cintf); - /* FIXME: Unbind if pre_reset returns an error or isn't defined */ + if (drv->pre_reset && drv->post_reset) + unbind = (drv->pre_reset)(cintf); + else if (cintf->condition == + USB_INTERFACE_BOUND) + unbind = 1; + if (unbind) + usb_forced_unbind_intf(cintf); } } } @@ -3404,13 +3414,18 @@ int usb_reset_device(struct usb_device *udev) for (i = config->desc.bNumInterfaces - 1; i >= 0; --i) { struct usb_interface *cintf = config->interface[i]; struct usb_driver *drv; + int rebind = cintf->needs_binding; - if (cintf->dev.driver) { + if (!rebind && cintf->dev.driver) { drv = to_usb_driver(cintf->dev.driver); if (drv->post_reset) - (drv->post_reset)(cintf); - /* FIXME: Unbind if post_reset returns an error or isn't defined */ + rebind = (drv->post_reset)(cintf); + else if (cintf->condition == + USB_INTERFACE_BOUND) + rebind = 1; } + if (rebind) + usb_rebind_intf(cintf); } } diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 1a8bc21c335e..d3eb0a29bca1 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -29,6 +29,8 @@ extern int usb_choose_configuration(struct usb_device *udev); extern void usb_kick_khubd(struct usb_device *dev); extern int usb_match_device(struct usb_device *dev, const struct usb_device_id *id); +extern void usb_forced_unbind_intf(struct usb_interface *intf); +extern void usb_rebind_intf(struct usb_interface *intf); extern int usb_hub_init(void); extern void usb_hub_cleanup(void); diff --git a/include/linux/usb.h b/include/linux/usb.h index 3cc8db5254d1..5811c5da69f9 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -160,6 +160,7 @@ struct usb_interface { unsigned is_active:1; /* the interface is not suspended */ unsigned sysfs_files_created:1; /* the sysfs attributes exist */ unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */ + unsigned needs_binding:1; /* needs delayed unbind/rebind */ struct device dev; /* interface specific device info */ struct device *usb_dev; -- cgit v1.2.3